{"record":{"id":"5ec7bb039c4baef8","repo":"HeyPuter/puter","slug":"app-or-api-token-required","errorCode":"app_or_api_token_required","errorMessage":"This API cannot be called with an account session token. Use an app or worker token, or create an API token from the dashboard (Account → API Token).","messagePattern":"This API cannot be called with an account session token\\. Use an app or worker token, or create an API token from the dashboard \\(Account → API Token\\)\\.","errorType":"exception","errorClass":"HttpError","httpStatus":403,"severity":"error","filePath":"src/backend/core/http/middleware/gates.ts","lineNumber":177,"sourceCode":" * This gate only rejects the bare-session shape. Which delegated credentials\n * are acceptable is decided by the gates it composes with (`requireUserActor`\n *\n * - `allowFullAccessToken` to also keep apps out, `requireNonAccessTokenGate` for\n *   scoped tokens, etc.).\n */\nexport const assertNotUserSession = (\n    actor: Pick<Actor, 'app' | 'accessToken' | 'session'> | null | undefined,\n): void => {\n    if (!actor) return; // anonymous requests are the auth gate's problem\n    if (actor.app || actor.accessToken) return;\n    // User-scoped workers (deployed with no app binding) authenticate with\n    // a session-TYPE token whose session row is `kind='worker'` — a managed,\n    // revocable deployment credential, not a browser sign-in. Workers are\n    // never treated as root tokens: this gate is an annoyance for\n    // sign-up-and-scrape abuse, and someone who deploys a worker to reach\n    // an API has already left that path.\n    if (actor.session?.kind === 'worker') return;\n    throw new HttpError(\n        403,\n        'This API cannot be called with an account session token. ' +\n            'Use an app or worker token, or create an API token from the ' +\n            'dashboard (Account → API Token).',\n        { legacyCode: 'app_or_api_token_required' },\n    );\n};\n\n/** Route-option form of {@link assertNotUserSession} (`noUserSession: true`). */\nexport const noUserSessionGate = (): RequestHandler => {\n    return (req, _res, next) => {\n        const actor = req.actor;\n        if (!actor) {\n            next(rejectAuth(req));\n            return;\n        }\n        try {\n            assertNotUserSession(actor);","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/core/http/middleware/gates.ts#L159-L195","documentation":"Raised by the `noUserSession` / `assertNotUserSession` gate: the actor is a bare browser session token (no app, no access token, and the session is not a `kind='worker'` session). Routes opt into this gate to forbid the 'root' session credential from doubling as an API/AI credential, pushing callers toward a delegated, revocable token instead.","triggerScenarios":"Calling an AI/API route (registered with `noUserSession: true`) while authenticated only via a browser session cookie or a token copied from `/login` — no app token, no access token, no worker session.","commonSituations":"A dev script that grabbed the session cookie to call the AI API; a puter.js token reused server-side; trying to drive an API endpoint with the sign-in token instead of minting an API token.","solutions":["Create an API token from the dashboard (Account → API Token) and send it as the auth credential.","Or register an app and call the route through its app token.","Or deploy a worker and use its worker session token (kind='worker'), which is explicitly allowed.","Stop reusing the browser session token for API/AI calls."],"exampleFix":"// before\nfetch('/drivers/call', { headers: { Cookie: sessionCookie } });\n// after\nfetch('/drivers/call', { headers: { Authorization: `Bearer ${apiToken}` } });","handlingStrategy":"validation","validationCode":"// Before calling a noUserSession route, ensure you're using a delegated credential:\nif (!apiToken && !appToken && !workerToken) {\n  throw new Error('Route requires an app/API/worker token, not a browser session.');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mint an API token from the dashboard for scripts and servers.","Never copy the browser session cookie into a server-side call.","Tag browser-session-only vs. delegated-token routes in your client wrappers."],"tags":["auth","token","api-token","gate","session"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}