vercel/ai · error · Error

Cartesia realtime client secret response did not include a t

Error message

Cartesia realtime client secret response did not include a token.

What it means

If the /access-token request succeeds (2xx) but the parsed JSON body does not contain a non-empty string token field, doCreateClientSecret throws. The client treats a missing/empty token as unusable — a client secret cannot be issued without it, so this indicates an unexpected success response shape.

Source

Thrown at packages/cartesia/src/cartesia-realtime-model.ts:110

        ...this.config.headers(),
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        grants: { stt: true },
        ...(expiresIn != null ? { expires_in: expiresIn } : {}),
      }),
    });

    if (!response.ok) {
      const text = await response.text();
      throw new Error(
        `Cartesia realtime client secret request failed: ${response.status} ${text}`,
      );
    }

    const data = (await response.json()) as { token?: unknown };
    if (typeof data.token !== 'string' || data.token.length === 0) {
      throw new Error(
        'Cartesia realtime client secret response did not include a token.',
      );
    }

    const turnDetection = sessionConfig?.turnDetection;
    const useTurnDetection =
      turnDetection === undefined ||
      (turnDetection !== null && turnDetection.type !== 'disabled');
    const inputAudioFormat = sessionConfig?.inputAudioFormat;
    const url = new URL(
      useTurnDetection
        ? `${this.config.baseURL}/stt/turns/websocket`
        : `${this.config.baseURL}/stt/websocket`,
    );
    url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:';
    url.searchParams.set('model', this.modelId);
    url.searchParams.set(
      'encoding',

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Log the raw response body to see what the endpoint actually returned under a 200 status.
  2. Verify this.config.baseURL points to the genuine Cartesia API (https://api.cartesia.ai), not a proxy or wrong region URL.
  3. Check your Cartesia account/plan includes realtime access-token issuance.
  4. Upgrade @ai-sdk/cartesia in case the token response schema changed and the provider needs an update.
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the endpoint returns JSON with a token before trusting it:
const res = await fetch(`${baseURL}/access-token`, { method: 'POST' });
const ct = res.headers.get('content-type') ?? '';
if (!ct.includes('application/json')) console.error('Non-JSON response from /access-token — check baseURL');

Type guard

function hasToken(data: unknown): data is { token: string } {
  return typeof data === 'object' && data !== null &&
    'token' in data && typeof (data as { token: unknown }).token === 'string' &&
    (data as { token: string }).token.length > 0;
}

Try / catch

try {
  return await model.doCreateClientSecret(options);
} catch (e) {
  if (/did not include a token/.test(String(e.message))) {
    // verify baseURL/plan, then retry once
    return retry(() => model.doCreateClientSecret(options), 1);
  }
  throw e;
}

Prevention

When it happens

Trigger: Cartesia returns 200 with a body lacking token (schema change, HTML error page served with 200, empty body, or a body keyed differently, e.g. { secret: ... }).

Common situations: Misconfigured baseURL pointing at a non-Cartesia endpoint that returns 200 HTML; authenticated but under-privileged key returning an empty success payload; API version drift.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/d528d2dc6f2d7bc7. Report an issue: GitHub.