{"record":{"id":"73309472681a42e8","repo":"HeyPuter/puter","slug":"unauthorized-733094","errorCode":"unauthorized","errorMessage":"Authentication required","messagePattern":"Authentication required","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"critical","filePath":"src/backend/drivers/ai-speech2speech/VoiceChangerDriver.ts","lineNumber":140,"sourceCode":"                400,\n                `Speech-to-speech provider not found: ${args.provider}. Available: ${PROVIDERS.join(', ')}`,\n                { legacyCode: 'bad_request' },\n            );\n        }\n\n        if (args.test_mode) {\n            return { url: SAMPLE_AUDIO_URL, content_type: 'audio/mpeg' };\n        }\n\n        if (!this.#apiKey) {\n            throw new HttpError(500, 'ElevenLabs API key not configured', {\n                legacyCode: 'internal_error',\n            });\n        }\n\n        const actor = Context.get('actor');\n        if (!actor)\n            throw new HttpError(401, 'Authentication required', {\n                legacyCode: 'unauthorized',\n            });\n\n        if (!args.audio) {\n            throw new HttpError(400, '`audio` is required', {\n                legacyCode: 'bad_request',\n            });\n        }\n\n        const loaded = await loadFileInput(\n            this.stores,\n            this.services.fs,\n            actor,\n            args.audio,\n            { maxBytes: MAX_AUDIO_FILE_SIZE },\n        );\n\n        const modelId = args.model_id || args.model || this.#defaultModelId;","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-speech2speech/VoiceChangerDriver.ts#L122-L158","documentation":"Before converting, the driver reads Context.get('actor') — the request-scoped authenticated user from AsyncLocalStorage. If no actor is present (the request reached the driver without a valid user session), it throws 401 / unauthorized. This is the driver-level in-depth auth gate; the controller route is expected to enforce auth too, but the driver defends itself.","triggerScenarios":"Invoking the driver outside an authenticated request context: a background job, a unit test calling convert() directly, or a controller route that skipped the auth RouteOptions gate.","commonSituations":"Calling convert() in a Vitest unit test without booting the ALS Context; a route registered without auth middleware; an expired or missing session token reaching the driver.","solutions":["Ensure the call runs inside an authenticated HTTP request so the actor is set on the ALS Context.","In tests use setupPuterTestEnv() and hit the real route so the actor is populated.","Verify the controller route declares the auth gate in its RouteOptions."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// client-side (puter.js)\nif (!puter.auth.isSignedIn()) {\n  await puter.auth.signIn();\n}","typeGuard":null,"tryCatchPattern":"try {\n  await puter.ai.speech2speech(/* ... */);\n} catch (e) {\n  if (e?.code === 'unauthorized' || e?.status === 401) {\n    await reAuthenticate();\n  } else throw e;\n}","preventionTips":["Always check puter.auth.isSignedIn() before user-scoped AI calls.","Don't invoke drivers from contexts that skip the ALS Context bootstrap (background jobs, raw unit tests).","Register the auth gate on every controller route that reaches the driver."],"tags":["auth","authentication","context","speech2speech"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}