Summary
When security.jwt_enabled: true, the async job endpoints registered by deploy/docker/job.py — POST /llm/job, GET /llm/job/{task_id}, POST /crawl/job, GET /crawl/job/{task_id} — return HTTP 500 for every request, with or without a Bearer token, making them unusable. The synchronous endpoints in server.py (/md, /crawl, /ask, /llm/{url}, …) are unaffected and correctly return 401.
Root cause
The job endpoints wire the auth dependency as:
_td: Dict = Depends(lambda: _token_dep()) # deploy/docker/job.py
Wrapping _token_dep in a lambda prevents FastAPI from resolving the credentials sub-dependency declared inside jwt_required (credentials: HTTPAuthorizationCredentials = Depends(security)). At request time verify_token receives a Depends object instead of the resolved credentials:
File "/app/job.py", line 59, in <lambda>
_td: Dict = Depends(lambda: _token_dep()),
File "/app/auth.py", line 66, in jwt_required
return verify_token(credentials)
File "/app/auth.py", line 34, in verify_token
if not credentials or not credentials.credentials:
AttributeError: 'Depends' object has no attribute 'credentials'
By contrast, server.py injects the same dependency correctly as Depends(token_dep) (no lambda). With jwt_enabled: false the bug is masked, because _token_dep is lambda: None and the wrapped call returns None.
Reproduction (Docker)
config.yml with JWT enabled:
security:
enabled: false
jwt_enabled: true
api_token: "TESTTOK"
docker run -d -p 11235:11235 -e SECRET_KEY=notsecret \
-v $PWD/config.yml:/app/config.yml unclecode/crawl4ai:latest
# no token -> expect 401, actually 500
curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:11235/llm/job \
-H 'Content-Type: application/json' -d '{"url":"https://example.com","q":"hi"}'
# -> 500
# mint a token, then retry with Authorization: Bearer <jwt> -> still 500
Affected versions
Reproduced on unclecode/crawl4ai:latest (0.8.6). The same Depends(lambda: _token_dep()) pattern is present on all four job routes in main.
Fix
PR incoming — replaces the broken lambda with a module-level dependency that mirrors auth.get_token_dependency (declares credentials properly, enforces a token when jwt_enabled is true, no-op when false).
Summary
When
security.jwt_enabled: true, the async job endpoints registered bydeploy/docker/job.py—POST /llm/job,GET /llm/job/{task_id},POST /crawl/job,GET /crawl/job/{task_id}— return HTTP 500 for every request, with or without a Bearer token, making them unusable. The synchronous endpoints inserver.py(/md,/crawl,/ask,/llm/{url}, …) are unaffected and correctly return 401.Root cause
The job endpoints wire the auth dependency as:
Wrapping
_token_depin alambdaprevents FastAPI from resolving thecredentialssub-dependency declared insidejwt_required(credentials: HTTPAuthorizationCredentials = Depends(security)). At request timeverify_tokenreceives aDependsobject instead of the resolved credentials:By contrast,
server.pyinjects the same dependency correctly asDepends(token_dep)(no lambda). Withjwt_enabled: falsethe bug is masked, because_token_depislambda: Noneand the wrapped call returnsNone.Reproduction (Docker)
config.ymlwith JWT enabled:Affected versions
Reproduced on
unclecode/crawl4ai:latest(0.8.6). The sameDepends(lambda: _token_dep())pattern is present on all four job routes inmain.Fix
PR incoming — replaces the broken lambda with a module-level dependency that mirrors
auth.get_token_dependency(declarescredentialsproperly, enforces a token whenjwt_enabledis true, no-op when false).