Skip to content

[Bug] /crawl/job and /llm/job return HTTP 500 when jwt_enabled=true #2016

@tuheyuan

Description

@tuheyuan

Bug Summary

When security.jwt_enabled: true is set in config.yml, the async job endpoints (POST /crawl/job, POST /llm/job, GET /crawl/job/{task_id}, GET /llm/job/{task_id}) return HTTP 500 Internal Server Error with AttributeError: 'Depends' object has no attribute 'credentials'.

All synchronous endpoints (/crawl, /md, /html, /screenshot, /pdf) work correctly with the same JWT.

Environment

  • crawl4ai: 0.8.9 (Docker image unclecode/crawl4ai:latest)
  • Source file: deploy/docker/job.py, line 100
  • Deployed via docker compose; bind mount config.yml; env SECRET_KEY set

Reproduce

config.yml:

security:
  enabled: true
  jwt_enabled: true
  api_token: "<64-char hex>"
# 1. Get JWT
JWT=$(curl -sS -X POST http://localhost:8890/token \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@gmail.com","api_token":"<above>"}' \
  | jq -r .access_token)

# 2. Sync endpoint — works ✅
curl -X POST http://localhost:8890/crawl \
  -H "Authorization: Bearer $JWT" \
  -H 'Content-Type: application/json' \
  -d '{"urls":["https://example.com"]}'
# → 200 OK

# 3. Async endpoint — fails ❌
curl -X POST http://localhost:8890/crawl/job \
  -H "Authorization: Bearer $JWT" \
  -H 'Content-Type: application/json' \
  -d '{"urls":["https://example.com"]}'
# → 500 Internal Server Error

Stack Trace

[ERROR] Exception in ASGI application
Traceback (most recent call last):
  ...
  File "/app/server.py", line 263, in add_security_headers
    resp = await call_next(request)
  ...
  File "/app/auth.py", line 64, in verify_token
    if not credentials or not credentials.credentials:
                            ^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Depends' object has no attribute 'credentials'

Root Cause Analysis

In deploy/docker/job.py:

@router.post("/crawl/job", status_code=202)
async def crawl_job_enqueue(
        payload: CrawlJobPayload,
        background_tasks: BackgroundTasks,
        _td: Dict = Depends(lambda: _token_dep()),   # ← line 100, BUG
):

Compare with the working pattern in deploy/docker/server.py (e.g., line 341):

async def get_markdown(
    request: Request,
    body: MarkdownRequest,
    _td: Dict = Depends(token_dep),   # ← works
):

The other endpoints pass token_dep (a function reference) directly to Depends(). FastAPI then calls token_dep() to get the jwt_required function, which FastAPI further resolves as a sub-dependency so its internal Depends(security) is evaluated and credentials becomes a real HTTPAuthorizationCredentials.

The job.py endpoints use Depends(lambda: _token_dep()) — the lambda is the dep, returns the jwt_required function object. FastAPI does not recursively resolve that returned function as a sub-dependency, so when jwt_required runs, Depends(security) is still a raw Depends object instead of the resolved HTTPAuthorizationCredentials. Hence credentials.credentials blows up.

Same bug exists at lines 59 (/llm/job), 90 (/llm/job/{task_id} status), 126 (/crawl/job/{task_id} status).

Suggested Fix

Replace the Depends(lambda: _token_dep()) pattern with Depends(_token_dep) (no parens):

# job.py, lines 59, 90, 100, 126
_td: Dict = Depends(_token_dep),

This passes the dep function directly, letting FastAPI resolve it normally — same as how Depends(token_dep) works in server.py.

A small refactor is needed because _token_dep is set as a module-global by init_job_router(). The fix may need to capture _token_dep in a closure or use functools.partial.

Impact

  • Severity: Medium — async task endpoints are unusable when JWT auth is enabled
  • Workaround: Use synchronous /crawl (returns full result inline, not a task_id). Same parameters, just no async/polling pattern.
  • Affects: All 4 endpoints in deploy/docker/job.py

Tested Versions

  • crawl4ai 0.8.9 (Docker image unclecode/crawl4ai:latest, 2026-06-16)
  • Did not test on older versions, but the pattern appears in the latest source

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions