langgenius/dify · error · BaseError

access_denied

access_denied

Error message

authorization denied

What it means

Raised by DatasourceOAuthCallback.get (GET /oauth/plugin/{provider_id}/datasource/callback) when neither the context_id cookie nor the context_id query parameter is present. The callback is browser-redirected from the OAuth provider, which normally carries the context_id cookie set by the get-authorization-url endpoint. Its absence means the cookie was never set, was cleared, or the callback was invoked directly. Maps to HTTP 403 via werkzeug Forbidden.

Source

Thrown at cli/src/commands/auth/login/device-flow.ts:54

    device_code: code.device_code,
    client_id: opts.clientId ?? DEFAULT_CLIENT_ID,
  }

  while (true) {
    if (opts.clock.isCancelled()) throw expired()
    const result = await pollWithRetry(api, req, opts.clock)
    switch (result.status) {
      case 'approved':
        return result.success
      case 'pending':
        break
      case 'slow_down':
        interval = Math.min(interval * 2, MAX_INTERVAL_MS)
        break
      case 'expired':
        throw expired()
      case 'denied':
        throw new BaseError({
          code: ErrorCode.AccessDenied,
          message: 'authorization denied',
        })
      case 'retry_5xx':
        throw new BaseError({
          code: ErrorCode.Server5xx,
          message: 'device-flow poll unavailable after retries',
        })
    }
    await opts.clock.sleepMs(interval)
    if (opts.clock.isCancelled()) throw expired()
  }
}

async function pollWithRetry(
  api: DeviceFlowApiSubset,
  req: PollRequest,
  clock: Clock,

View on GitHub (pinned to ef8544b173)

Solutions

  1. Always initiate the flow via GET /oauth/plugin/{provider_id}/datasource/get-authorization-url so the context_id cookie is set first.
  2. Ensure the browser accepts first-party cookies for the console domain (check SameSite=Lax cookie behavior).
  3. If the cookie expired, restart the OAuth flow from the authorization-url endpoint.
  4. For programmatic testing, pass context_id as a query parameter matching one obtained from create_proxy_context.

Example fix

// before — opening the callback URL directly
window.location = `/console/api/oauth/plugin/${providerId}/datasource/callback?code=${code}`;
// after — start from the authorization-url endpoint so the cookie is set
window.location = `/console/api/oauth/plugin/${providerId}/datasource/get-authorization-url`;
Defensive patterns

Strategy: validation

Validate before calling

function hasContextId(): boolean {
  return Boolean(getCookie('context_id') || new URLSearchParams(location.search).get('context_id'));
}
if (!hasContextId()) {
  // start the OAuth flow from the top
  window.location = `/console/api/oauth/plugin/${providerId}/datasource/get-authorization-url`;
}

Type guard

function isOAuthInitiated(contextId: string | null | undefined): contextId is string {
  return typeof contextId === 'string' && contextId.length > 0;
}

Try / catch

try {
  await fetch(callbackUrl);
} catch (e) {
  if (e.response?.status === 403 && /context_id not found/i.test(e.response.data?.message || '')) {
    // restart the flow to set the cookie
    window.location = `/console/api/oauth/plugin/${providerId}/datasource/get-authorization-url`;
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: OAuth provider redirects back to /callback but the user's browser dropped the context_id cookie (third-party cookie blocking, different domain, incognito); a developer manually opens the callback URL in a new tab without first hitting get-authorization-url; the cookie expired (max_age = OAuthProxyService.__MAX_AGE__).

Common situations: Browser blocks third-party/same-site cookies because the OAuth provider redirects across origins; Safari ITP stripping the cookie; user copied the callback URL instead of following the redirect flow; long delay between authorization and callback exceeding cookie max_age.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/66fcd48570c607e9. Report an issue: GitHub.