vercel/ai · error · Error
Cartesia realtime client secrets must expire between 1 and 3
Error message
Cartesia realtime client secrets must expire between 1 and 3600 seconds.
What it means
doCreateClientSecret validates options.expiresAfterSeconds client-side before requesting an access token. If the value is provided but is not a positive integer within 1–3600, a plain Error is thrown. This mirrors the Cartesia API's own constraint on client-secret lifetimes.
Source
Thrown at packages/cartesia/src/cartesia-realtime-model.ts:77
constructor(
modelId: CartesiaRealtimeModelId,
config: CartesiaRealtimeModelConfig,
) {
this.modelId = modelId;
this.provider = config.provider;
this.config = config;
}
async doCreateClientSecret(
options: RealtimeModelV4ClientSecretOptions,
): Promise<RealtimeModelV4ClientSecretResult> {
const expiresIn = options.expiresAfterSeconds;
if (
expiresIn != null &&
(!Number.isInteger(expiresIn) || expiresIn <= 0 || expiresIn > 3600)
) {
throw new Error(
'Cartesia realtime client secrets must expire between 1 and 3600 seconds.',
);
}
const sessionConfig = options.sessionConfig;
const language = sessionConfig?.inputAudioTranscription?.language;
if (language != null && language !== 'en') {
throw new Error('Cartesia Ink 2 currently supports English only.');
}
const fetchFn = this.config.fetch ?? fetch;
const response = await fetchFn(`${this.config.baseURL}/access-token`, {
method: 'POST',
headers: {
...this.config.headers(),
'Content-Type': 'application/json',
},
body: JSON.stringify({View on GitHub (pinned to 69428b1f8b)
Solutions
- Set expiresAfterSeconds to an integer between 1 and 3600, or omit the option entirely for the default expiry.
- Clamp/validate the value before calling: Math.min(3600, Math.max(1, seconds)).
- If 0 was intended as 'no expiry', remove the expiresAfterSeconds field instead.
Example fix
// before
const secret = await model.doCreateClientSecret({ expiresAfterSeconds: 7200 });
// after
const secret = await model.doCreateClientSecret({ expiresAfterSeconds: 3600 }); Defensive patterns
Strategy: validation
Validate before calling
function isValidExpiry(s: unknown): boolean {
return s == null || (Number.isInteger(s) && s > 0 && s <= 3600);
}
// call before requesting the secret
if (!isValidExpiry(opts.expiresAfterSeconds)) throw new RangeError('expiresAfterSeconds must be 1-3600 or omitted'); Type guard
function isValidExpirySeconds(v: unknown): v is number | undefined {
return v === undefined || (typeof v === 'number' && Number.isInteger(v) && v > 0 && v <= 3600);
} Try / catch
try {
const secret = await model.doCreateClientSecret(options);
} catch (e) {
if (e instanceof RangeError || /1 and 3600 seconds/.test(String(e.message))) {
// clamp and retry once
const clamped = Math.min(3600, Math.max(1, Math.floor(Number(options.expiresAfterSeconds) || 3600)));
return model.doCreateClientSecret({ ...options, expiresAfterSeconds: clamped });
}
throw e;
} Prevention
- Remember the unit is seconds, not minutes or milliseconds.
- Omit expiresAfterSeconds rather than passing 0 when you want the default.
- Clamp user/config-derived values to [1, 3600] before calling.
When it happens
Trigger: cartesia.realtime(modelId).doCreateClientSecret({ expiresAfterSeconds: 0 | -5 | 4000 | 12.5 | ... }) — any non-integer, non-positive, or >3600 value.
Common situations: Computing the expiry in minutes and passing seconds incorrectly (e.g. 7200 instead of 3600); passing 0 thinking it means 'no expiry' instead of omitting the field; passing a string parsed value that isn't an integer.
Related errors
- Cartesia Ink 2 currently supports English only.
- maxEmbeddingsPerCall must be greater than 0
- maxInputBytesPerCall must be greater than 0
- No image generated.
- No object generated: the model did not return a response.
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/d9827624491b1321.
Report an issue: GitHub.