{"record":{"id":"c7179061f44d0685","repo":"HeyPuter/puter","slug":"unauthorized-c71790","errorCode":"unauthorized","errorMessage":"Authentication required","messagePattern":"Authentication required","errorType":"exception","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"src/backend/drivers/ai-chat/ChatCompletionDriver.ts","lineNumber":307,"sourceCode":"                    if (typeof raw !== 'number' || !Number.isFinite(raw))\n                        continue;\n                    out.push({\n                        usageType: `${model.provider}:${model.id}:${costKey}`,\n                        ucentsPerUnit: raw,\n                        unit: 'token',\n                        source: `driver:aiChat/${model.provider}`,\n                        costs_currency: model.costs_currency,\n                    });\n                }\n            }\n        }\n        return out;\n    }\n\n    async complete(args: ICompleteArguments): Promise<IChatCompleteResult> {\n        const actor = Context.get('actor');\n        if (!actor)\n            throw new HttpError(401, 'Authentication required', {\n                legacyCode: 'unauthorized',\n            });\n\n        let intendedProvider = args.provider || '';\n        if (!args.model && !intendedProvider) {\n            intendedProvider = 'azure-openai'; // default provider\n        }\n        if (\n            !args.model &&\n            intendedProvider &&\n            this.#providers[intendedProvider]\n        ) {\n            args.model = this.#providers[intendedProvider].getDefaultModel();\n        }\n\n        let model = this.#resolveModel(args.model, intendedProvider);\n        if (!model) {\n            throw new HttpError(400, `Model not found: ${args.model}`, {","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-chat/ChatCompletionDriver.ts#L289-L325","documentation":"Raised at the top of the AI chat `complete()` driver when `Context.get('actor')` is null — there is no authenticated actor in the request-scoped AsyncLocalStorage. The AI endpoint requires authentication; anonymous completion is not permitted.","triggerScenarios":"Calling the AI chat complete driver/endpoint without auth, or with auth middleware that didn't populate the actor; or a code path that lost the Context (ALS) across an async boundary so `Context.get('actor')` returns undefined.","commonSituations":"Missing auth header/token; the route isn't behind the auth gate; a custom integration calling the driver directly outside a request context; ALS context dropped by an unawaited/incorrectly-bound callback.","solutions":["Authenticate before calling the AI endpoint (send the app/API/session token).","Ensure the route runs the auth gate that sets the actor in Context.","If calling the driver from non-request code, wrap the call in a Context.run(...) that sets actor.","Verify ALS propagation across awaited boundaries."],"exampleFix":"// before\nputer.ai.chat('hi'); // no auth\n// after — ensure a token is set first\nputer.setAuthToken(apiToken);\nputer.ai.chat('hi');","handlingStrategy":"validation","validationCode":"// Ensure an auth token is present before calling AI:\nif (!hasAuthToken()) { throw new Error('Authentication required for AI calls'); }","typeGuard":null,"tryCatchPattern":"try { await aiChat(); }\ncatch (e) {\n  if (e.code === 'unauthorized') { routeToLogin(); return; }\n  throw e;\n}","preventionTips":["Always authenticate before AI calls.","Confirm the route is behind the auth gate that sets the actor.","When calling the driver outside a request, wrap it in a Context that sets actor."],"tags":["auth","ai","chat","context","unauthorized"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}