Error handling
Failures surface as a FinnError with a code you can switch on. The same
error is raised from the streaming loop and from turn.completed, and 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; }}from finn_sdk import FinnError
try: turn = await finn.send_message("How many active members do I have?") async for chunk in turn: print(chunk.text, end="", flush=True) await turn.completedexcept FinnError as err: print(f"Finn failed ({err.code}): {err.message}")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}class FinnError(Exception): code: str # see the table below message: str conversation_id: str | None # the affected conversation, when known details: object | None # extra backend context, when presentError codes
Section titled “Error codes”code | Meaning | What to do |
|---|---|---|
unauthorized | API key missing or rejected. | Check apiKey/api_key or getApiKey/get_api_key. 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/timeout_ms. |
conversation_read_only | The target conversation can’t accept new messages. | Start a new conversation (omit the conversation id). |
stream_error | The turn failed mid-generation. | Retry the question. |
disconnected | The socket dropped during the turn. | With reconnection on it reconnects; re-send using the conversation id. |
invalid_json | askJson() / ask_json() couldn’t parse the answer as JSON. | Inspect error.details.raw / 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); }}except FinnError as err: if err.code == "unauthorized": return prompt_for_new_key() elif err.code in ("rate_limited", "timeout"): return retry_with_backoff() elif err.code == "conversation_read_only": return start_new_conversation() else: return show_error(err.message)