moeru-ai/airi · error · Error
streaming-tts: not authenticated
Error message
streaming-tts: not authenticated
What it means
Thrown synchronously at the top of streamingSynthesize when no auth token is available. The function needs a Bearer JWT to open the WebSocket at /api/v1/audio/speech/ws; it first checks options.token then falls back to getAuthToken(). If both are falsy it throws before constructing the ws URL, preventing an unauthenticated connection attempt.
Source
Thrown at packages/stage-ui/src/libs/speech/streaming-session.ts:91
*
* Use when:
* - The stage speech pipeline's per-segment `tts()` callback wants to use
* the streaming gateway instead of HTTP `/audio/speech`.
*
* Expects:
* - The user is authenticated; `getAuthToken()` returns a JWT or one is
* passed in `options.token`.
* - The server has `STREAMING_TTS_UPSTREAM` configured.
*
* Returns:
* - `{ audio, sentences, byteLength }` once `session.finished` arrives.
* - Rejects with the upstream `error.message` on a server error event.
* - Rejects with the abort reason on signal abort.
*/
export async function streamingSynthesize(options: StreamingTtsSessionOptions): Promise<StreamingTtsSessionResult> {
const token = options.token ?? getAuthToken()
if (!token)
throw new Error('streaming-tts: not authenticated')
const baseUrl = options.serverUrl ?? SERVER_URL
const wsUrl = toWebSocketUrl(baseUrl, '/api/v1/audio/speech/ws', token, {
ttsTrigger: options.ttsTrigger ?? 'manual',
ttsSource: options.ttsSource ?? 'manual_preview',
ttsVoiceType: options.ttsVoiceType ?? 'unknown',
})
const audioChunks: ArrayBuffer[] = []
const sentences: StreamingTtsSessionResult['sentences'] = []
let totalBytes = 0
return new Promise<StreamingTtsSessionResult>((resolve, reject) => {
const ws = new WebSocket(wsUrl)
ws.binaryType = 'arraybuffer'
let settled = false
function settle(action: () => void) {View on GitHub (pinned to 27111382b4)
Solutions
- Ensure the user is authenticated before invoking streamingSynthesize; gate the call on a non-empty auth token.
- If calling during startup, await auth hydration before initializing the speech pipeline.
- Pass an explicit options.token when orchestrating server-side or with a freshly refreshed token.
- Catch this error and redirect the user to login rather than retrying blindly.
Example fix
// before
const { audio } = await streamingSynthesize({ model, voice, input })
// after: guard on auth state
const token = getAuthToken()
if (!token) {
// prompt login / skip TTS
return
}
const { audio } = await streamingSynthesize({ model, voice, input, token }) Defensive patterns
Strategy: validation
Validate before calling
import { getAuthToken } from '../auth'
function canStreamTts(tokenOverride?: string): boolean {
return !!(tokenOverride ?? getAuthToken())
}
// before calling
if (!canStreamTts(options.token)) {
// prompt login or skip
return
}
await streamingSynthesize(options) Type guard
function hasStreamingTtsCredentials(options: StreamingTtsSessionOptions): boolean {
return !!(options.token ?? getAuthToken())
} Try / catch
try {
const { audio } = await streamingSynthesize(options)
}
catch (error) {
if (String(error).includes('not authenticated')) {
// redirect to login / refresh token
}
throw error
} Prevention
- Await auth hydration before initializing the speech pipeline at startup.
- Gate streaming TTS UI controls on a non-empty auth token.
- Pass an explicitly refreshed token via options.token when orchestrating server-side.
When it happens
Trigger: Calling streamingSynthesize({ model, voice, input }) while the user is not logged in (getAuthToken() returns null/empty) and options.token is omitted; the auth store has not been hydrated yet on app startup; the session expired and the token was cleared.
Common situations: TTS invoked during the brief window before auth bootstrap completes; logged-out user triggering streaming TTS from settings test or chat auto-TTS; token refresh failed silently leaving getAuthToken empty; misordered app init where speech pipeline starts before auth.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- updateUser failed
- Invalid WebSocket event format.
- Invalid AIRI websocket message.
- Missing input text for Gemini TTS
- Missing model for Gemini TTS
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/4cd65fb53071a05c.
Report an issue: GitHub.