Skip to content

Error handling

Failures surface as a FinnError — a typed Error subclass with a code you can switch on. The same error is thrown from the streaming loop and from turn.completed, and rejected from connect().

import { FinnError } from '@riseanalytics/finn-sdk';
try {
const turn = finn.sendMessage('How many active members do I have?');
for await (const chunk of turn) process.stdout.write(chunk.text);
await turn.completed;
} catch (err) {
if (err instanceof FinnError) {
console.error(`Finn failed (${err.code}): ${err.message}`);
} else {
throw err;
}
}
class FinnError extends Error {
code: FinnErrorCode; // see the table below
conversationId?: string; // the affected conversation, when known
details?: unknown; // extra backend context, when present
}
codeMeaningWhat to do
unauthorizedAPI key missing or rejected.Check apiKey / getApiKey. Don’t retry blindly.
rate_limitedYou hit a rate limit.Back off and retry; inspect the rateLimit event for hints.
timeoutConnect or turn exceeded its time budget.Retry; consider a longer timeoutMs.
conversation_read_onlyThe target conversation can’t accept new messages.Start a new conversation (omit conversationId).
stream_errorThe turn failed mid-generation.Retry the question.
disconnectedThe socket dropped during the turn.With reconnection: true it reconnects; re-send using the conversationId.
invalid_jsonaskJson() couldn’t parse the answer as JSON.Inspect error.details.raw; tighten your “JSON only” prompt or fall back to ask().
catch (err) {
if (!(err instanceof FinnError)) throw err;
switch (err.code) {
case 'unauthorized':
return promptForNewKey();
case 'rate_limited':
case 'timeout':
return retryWithBackoff();
case 'conversation_read_only':
return startNewConversation();
default:
return showError(err.message);
}
}