bytedance/deer-flow · error · HTTPException

CSRF token missing. Include X-CSRF-Token header.

Error message

CSRF token missing. Include X-CSRF-Token header.

What it means

HTTP 403 from the LangGraph-compatible runtime's CSRF guard: for state-changing methods (POST, PUT, DELETE, PATCH), when auth is enabled, the request must carry BOTH a csrf_token cookie and an X-CSRF-Token header. Missing either raises 'CSRF token missing. Include X-CSRF-Token header.' This is double-submit-cookie CSRF protection on the proxied /api/langgraph/* surface.

Source

Thrown at backend/app/gateway/langgraph_auth.py:49

def _check_csrf(request) -> None:
    """Enforce Double Submit Cookie CSRF check for state-changing requests.

    Mirrors Gateway's CSRFMiddleware logic so that LangGraph routes
    proxied directly by nginx have the same CSRF protection.
    """
    method = getattr(request, "method", "") or ""
    if method.upper() not in _CSRF_METHODS:
        return

    if is_auth_disabled():
        return

    cookie_token = request.cookies.get("csrf_token")
    header_token = request.headers.get("x-csrf-token")

    if not cookie_token or not header_token:
        raise Auth.exceptions.HTTPException(
            status_code=403,
            detail="CSRF token missing. Include X-CSRF-Token header.",
        )

    if not secrets.compare_digest(cookie_token, header_token):
        raise Auth.exceptions.HTTPException(
            status_code=403,
            detail="CSRF token mismatch.",
        )


@auth.authenticate
async def authenticate(request):
    """Validate the session cookie, decode JWT, and check token_version.

    Same validation chain as Gateway's get_current_user_from_request:
      cookie → decode JWT → DB lookup → token_version match
    Also enforces CSRF on state-changing methods.

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Read csrf_token from the login response cookies and send it back on every mutating call: header 'X-CSRF-Token: <value>' with credentials included
  2. In browser code use a shared fetch wrapper that attaches the header from document.cookie for POST/PUT/DELETE/PATCH
  3. For curl: curl -b jar -c jar -H "X-CSRF-Token: $(awk '/csrf_token/{print $7}' jar)" ...
  4. Ensure CORS config (if cross-origin) allows the X-CSRF-Token request header

Example fix

// before: 403 CSRF token missing
await fetch('/api/langgraph/threads', {
  method: 'POST', credentials: 'include',
  headers: { 'content-type': 'application/json' }, body: '{}',
});
// after
const csrf = document.cookie.match(/csrf_token=([^;]+)/)?.[1];
await fetch('/api/langgraph/threads', {
  method: 'POST', credentials: 'include',
  headers: { 'content-type': 'application/json', 'X-CSRF-Token': csrf }, body: '{}',
});
Defensive patterns

Strategy: validation

Validate before calling

function csrfToken(): string | null {
  const m = document.cookie.match(/(?:^|; )csrf_token=([^;]+)/);
  return m ? decodeURIComponent(m[1]) : null;
}
// guard before any mutation
const t = csrfToken();
if (!t) throw new Error('no csrf cookie — login first');
fetch(url, { method: 'POST', credentials: 'include', headers: { 'X-CSRF-Token': t } });

Type guard

null

Try / catch

catch (e) {
  if (e.status === 403 && e.detail?.includes('CSRF token missing')) {
    await refreshSession();  // obtain csrf cookie, then replay once
    return fetchOnce(url, opts);
  }
  throw e;
}

Prevention

When it happens

Trigger: POST/PUT/DELETE/PATCH to any LangGraph-runtime route (e.g. runs/threads mutations through nginx /api/langgraph/) without the X-CSRF-Token header, without the csrf_token cookie, or both — typically a non-browser client or a fetch that forwards cookies but not custom headers.

Common situations: Script/curl client that authenticates with cookies but never reads the Set-Cookie csrf_token to echo it in a header; frontend fetch omitting the X-CSRF-Token header after a refactor; CORS preflight stripping the custom header; auth disabled flag turned off (enabled auth) in an environment where the client was built assuming no CSRF.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/12a10b85ab6b921f. Report an issue: GitHub.