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; }}The shape of a FinnError
Section titled “The shape of a FinnError”class FinnError extends Error { code: FinnErrorCode; // see the table below conversationId?: string; // the affected conversation, when known details?: unknown; // extra backend context, when present}Error codes
Section titled “Error codes”code | Meaning | What to do |
|---|---|---|
unauthorized | API key missing or rejected. | Check apiKey / getApiKey. Don’t retry blindly. |
rate_limited | You hit a rate limit. | Back off and retry; inspect the rateLimit event for hints. |
timeout | Connect or turn exceeded its time budget. | Retry; consider a longer timeoutMs. |
conversation_read_only | The target conversation can’t accept new messages. | Start a new conversation (omit conversationId). |
stream_error | The turn failed mid-generation. | Retry the question. |
disconnected | The socket dropped during the turn. | With reconnection: true it reconnects; re-send using the conversationId. |
invalid_json | askJson() couldn’t parse the answer as JSON. | Inspect error.details.raw; tighten your “JSON only” prompt or fall back to ask(). |
Switching on the code
Section titled “Switching on the code”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); }}