danny-avila/LibreChat · error · Error

OAuth flow not found

Error message

OAuth flow not found

What it means

Thrown in the Action OAuth callback after CSRF validation succeeds but flowManager.getFlowState returns nothing. The transient OAuth flow record (created at the start of the flow) is no longer present, so the authorization code cannot be exchanged safely.

Source

Thrown at api/server/routes/actions.js:99

    identifier = `${decodedState.user}:${action_id}`;

    if (
      !validateOAuthCsrf(req, res, identifier, OAUTH_CSRF_COOKIE_PATH) &&
      !validateOAuthSession(req, decodedState.user)
    ) {
      logger.error('[Action OAuth] CSRF validation failed: no valid CSRF or session cookie', {
        identifier,
        hasCsrfCookie: !!req.cookies?.[OAUTH_CSRF_COOKIE],
        hasSessionCookie: !!req.cookies?.[OAUTH_SESSION_COOKIE],
      });
      await flowManager.failFlow(identifier, 'oauth', 'CSRF validation failed');
      return res.redirect(`${basePath}/oauth/error?error=csrf_validation_failed`);
    }

    const flowState = await flowManager.getFlowState(identifier, 'oauth');
    if (!flowState) {
      throw new Error('OAuth flow not found');
    }

    const tokenData = await getAccessToken(
      {
        code,
        userId: decodedState.user,
        identifier,
        client_url: flowState.metadata.client_url,
        redirect_uri: flowState.metadata.redirect_uri,
        token_exchange_method: flowState.metadata.token_exchange_method,
        allowedAddresses: flowState.metadata.allowedAddresses,
        /** Encrypted values */
        encrypted_oauth_client_id: flowState.metadata.encrypted_oauth_client_id,
        encrypted_oauth_client_secret: flowState.metadata.encrypted_oauth_client_secret,
      },
      {
        findToken,
        updateToken,

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Re-initiate the OAuth flow from the start so a fresh flow state is created.
  2. Increase the OAuth flow TTL if users consistently run past it on slow consent screens.
  3. Use a durable flow store (DB/redis) instead of in-memory so flows survive restarts.
  4. Detect duplicate callbacks and short-circuit before this code path.
Defensive patterns

Strategy: try-catch

Validate before calling

const flowState = await flowManager.getFlowState(identifier, 'oauth');
if (!flowState) {
  return res.redirect(`${basePath}/oauth/error?error=flow_not_found`);
}

Try / catch

try { /* callback handler */ }
catch (e) {
  if (/OAuth flow not found/.test(e.message)) {
    return res.redirect(`${basePath}/oauth/error?error=expired_flow`);
  }
  throw e;
}

Prevention

When it happens

Trigger: The OAuth flow state expired or was evicted from its store before the callback returned; a duplicate/old callback hits the endpoint after the flow was completed or failed; the flow was never created (callback reached without a matching identifier).

Common situations: User takes a long time on the OAuth consent screen and the flow TTL elapses; replayed callback URL (refresh/bookmark); flow store restart or flush (e.g. in-memory store lost on redeploy); clock skew between issuer and callback.

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/5cb8de469b4da408. Report an issue: GitHub.