@google/genai
semver
>=0.1.0postconditions13functions13last verified2026-04-16Postconditions: what we check
- generateContent · genai-generate-content-errorerrorWhenai.models.generateContent() called without try-catch or .catch() handlerThrows
ApiError (name='ApiError') with .status (HTTP status code) and .message. Common statuses: 400 (invalid request/model), 401 (missing API key), 403 (API key invalid/permission denied), 429 (quota exceeded/rate limit), 500 (internal server error), 503 (service unavailable).Required handlingCaller MUST wrap await ai.models.generateContent() in try-catch or chain .catch(). Uncaught ApiError causes unhandled promise rejection — AI features silently fail and users see broken responses or error pages. try { const response = await ai.models.generateContent({ model: 'gemini-2.0-flash', contents: prompt, }); return response.text; } catch (error) { if (error instanceof ApiError) { if (error.status === 429) throw new Error('Rate limited'); } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - generateContentStream · genai-generate-content-stream-errorerrorWhenai.models.generateContentStream() called without try-catch or .catch() handlerThrows
ApiError (name='ApiError') with .status and .message on HTTP failure. The initial await can throw before any chunks arrive.Required handlingCaller MUST wrap await ai.models.generateContentStream() in try-catch. Errors on the initial call propagate before any stream chunks are yielded.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - generateImages · genai-generate-images-errorerrorWhenai.models.generateImages() called without try-catch or .catch() handlerThrows
ApiError (name='ApiError') with .status and .message on HTTP failure.Required handlingCaller MUST wrap await ai.models.generateImages() in try-catch.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - embedContent · genai-embed-content-errorerrorWhenai.models.embedContent() called without try-catch or .catch() handlerThrows
ApiError (name='ApiError') with .status and .message on HTTP failure.Required handlingCaller MUST wrap await ai.models.embedContent() in try-catch. This is a common antipattern — seen unprotected in cherry-studio-app (2.9k stars).costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - sendMessage · genai-send-message-no-error-handlingerrorWhenchat.sendMessage() called in async context without surrounding try-catch. ApiError propagates as unhandled promise rejection.Throws
ApiError (name='ApiError') with .status (HTTP status code) and .message. Common statuses: 400 (invalid request/model), 401 (missing API key), 403 (API key invalid/permission denied), 429 (quota exceeded/rate limit), 500 (internal server error), 503 (service unavailable). Error (generic) for mimeType or history validation failures.Required handlingCaller MUST wrap await chat.sendMessage() in try-catch. Chat sessions are stateful — an unhandled error during a turn corrupts the conversational state. The SDK resets sendPromise on error, so subsequent sends can proceed, but the failed turn is recorded as invalid in comprehensive history. try { const response = await chat.sendMessage({ message: userInput }); return response.text; } catch (error) { if (error instanceof ApiError) { if (error.status === 429) { // Rate limit — retry with backoff } else if (error.status === 400) { // Invalid content — surface to user } } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - sendMessageStream · genai-send-message-stream-no-error-handlingerrorWhenchat.sendMessageStream() called in async context without surrounding try-catch. ApiError propagates as unhandled promise rejection on the initial await.Throws
ApiError (name='ApiError') with .status and .message on HTTP failure before any chunks arrive. Errors during streaming (mid-stream) also throw ApiError on the next chunk iteration.Required handlingCaller MUST wrap await chat.sendMessageStream() in try-catch. The initial await can throw before any chunks arrive. Streaming errors mid-response also propagate as ApiError on the async generator. try { const stream = await chat.sendMessageStream({ message: userInput }); for await (const chunk of stream) { process.stdout.write(chunk.text ?? ''); } } catch (error) { if (error instanceof ApiError) { // Handle network error or API failure } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - upload · genai-files-upload-no-error-handlingerrorWhenai.files.upload() called without try-catch. Upload protocol errors, quota exceeded, or API failures result in unhandled promise rejection.Throws
ApiError (name='ApiError') with .status and .message for HTTP failures (401 missing API key, 403 storage quota exceeded, 413 file too large, 429 rate limit, 500 server error). Error (generic) for mimeType inference failure ('Can not determine mimeType'), upload protocol failures ('Failed to get upload url'), finalization errors ('Failed to upload file: Upload status is not finalized'), or when called on Vertex AI ('Vertex AI does not support uploading files').Required handlingCaller MUST wrap await ai.files.upload() in try-catch. Large file uploads are particularly prone to failure mid-upload (network interruption, quota). The Files API has strict limits: 2 GB per file, 20 GB project storage. try { const file = await ai.files.upload({ file: '/path/to/video.mp4', config: { mimeType: 'video/mp4' }, }); // Use file.name in subsequent generateContent calls } catch (error) { if (error instanceof ApiError && error.status === 403) { throw new Error('File storage quota exceeded'); } throw error; }costhighin prodimmediate exceptionusers seelost datavisibilitysilent - countTokens · genai-count-tokens-no-error-handlingwarningWhenai.models.countTokens() called without try-catch. Used as pre-flight check to measure token count before sending large prompts. ApiError on invalid model name or API failure silently breaks the pre-flight guard.Throws
ApiError (name='ApiError') with .status and .message on HTTP failure. 404: model name not found or invalid format. 400: invalid content (e.g., empty contents array). 401: missing or invalid API key. 429: rate limit exceeded (countTokens has its own rate limits, separate from generateContent).Required handlingCaller MUST wrap await ai.models.countTokens() in try-catch. If the pre-flight check throws and the error is swallowed, the caller proceeds with an untested prompt that may exceed context limits. try { const result = await ai.models.countTokens({ model: 'gemini-2.0-flash', contents: largePrompt, }); if (result.totalTokens > 1000000) { throw new Error('Prompt too large for model context window'); } } catch (error) { if (error instanceof ApiError) { // Pre-flight failed — proceed with caution or abort } throw error; }costlowin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[9] - create · genai-caches-create-no-error-handlingerrorWhenai.caches.create() called without try-catch. API failures (unsupported model, token threshold not met, quota exceeded) result in unhandled promise rejection.Throws
ApiError (name='ApiError') with .status and .message on HTTP failure. 400: model does not support context caching, or content below minimum token threshold (1,024 tokens for Flash models, 4,096 for Pro models). 401: missing or invalid API key. 403: quota exceeded or billing issue. 404: model not found. 429: rate limit exceeded.Required handlingCaller MUST wrap await ai.caches.create() in try-catch. A 400 error indicates either an incompatible model or insufficient content — both require different resolution strategies. try { const cache = await ai.caches.create({ model: 'gemini-2.5-flash', config: { contents: largeSystemContext, ttl: '3600s', }, }); // Use cache.name in subsequent generateContent calls } catch (error) { if (error instanceof ApiError && error.status === 400) { // Either model doesn't support caching or content < minimum tokens } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - uploadToFileSearchStore · genai-file-search-stores-upload-errorerrorWhenai.fileSearchStores.uploadToFileSearchStore() called without try-catch. Three distinct throw paths: (1) Vertex AI client (always throws — method unsupported), (2) MIME type cannot be inferred (no extension on string path / no Blob.type), (3) upload location cannot be established (HTTP error from upload-protocol initiation).Throws
Error on Vertex AI client (method not supported). Error on MIME type inference failure when mimeType not provided in config. Error on upload-location establishment failure. ApiError on 4xx/5xx HTTP responses from upload-protocol layer.Required handlingCaller MUST wrap await ai.fileSearchStores.uploadToFileSearchStore() in try-catch. RAG ingestion pipelines that silently swallow these errors end up with empty file search stores and degraded retrieval quality that is hard to detect downstream. try { const op = await ai.fileSearchStores.uploadToFileSearchStore({ fileSearchStoreName: 'fileSearchStores/foo-bar', file: 'doc.pdf', config: { mimeType: 'application/pdf' }, }); // Poll op until op.done === true } catch (error) { if (error instanceof ApiError) { // HTTP error during upload-location establishment } throw error; }costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilent - importFile · genai-file-search-stores-import-file-errorerrorWhenai.fileSearchStores.importFile() called without try-catch.Throws
ApiError with .status and .message on HTTP failure during operation initiation. Common statuses: 400 (source file not found / unsupported format), 401 (missing API key), 403 (quota / permission), 404 (fileSearchStoreName not found), 429 (rate limit).Required handlingCaller MUST wrap await ai.fileSearchStores.importFile() in try-catch. The returned Operation must additionally be polled — chunking and embedding failures surface on the polled Operation.error field, not from the initial Promise. try { const op = await ai.fileSearchStores.importFile({ fileSearchStoreName: 'fileSearchStores/foo', fileName: 'files/bar', }); // Poll op via ai.operations.get() until op.done } catch (error) { if (error instanceof ApiError) { // Operation never started — bad request or auth failure } throw error; }costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilent - tune · genai-tunings-tune-errorerrorWhenai.tunings.tune() called without try-catch.Throws
ApiError (name='ApiError') with .status and .message on HTTP failure. Common statuses: 400 (invalid trainingDataset format / unsupported baseModel), 401 (missing API key), 403 (tuning quota exceeded / permission denied — fine-tuning requires elevated access), 404 (baseModel does not support tuning), 429 (rate limit on tuning submissions), 503 (tuning service unavailable).Required handlingCaller MUST wrap await ai.tunings.tune() in try-catch. Fine-tuning jobs cost money to run — a swallowed ApiError on submission means the caller incorrectly believes the job is queued and may double-submit or never poll for completion, masking the real failure. try { const job = await ai.tunings.tune({ baseModel: 'models/gemini-2.0-flash', trainingDataset: { gcsUri: 'gs://my-bucket/train.jsonl' }, config: { tunedModelDisplayName: 'my-tuned-model' }, }); // Poll ai.tunings.get({ name: job.name }) until terminal state } catch (error) { if (error instanceof ApiError) { if (error.status === 403) { // Quota or permission — escalate, don't retry blindly } } throw error; }costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilent - createEmbeddings · genai-batches-create-embeddings-errorerrorWhenai.batches.createEmbeddings() called without try-catch.Throws
ApiError with .status and .message on HTTP failure. Common statuses: 400 (invalid src format / model does not support batch embeddings), 401 (missing API key), 403 (batch quota exceeded), 404 (model not found), 429 (rate limit).Required handlingCaller MUST wrap await ai.batches.createEmbeddings() in try-catch. Batch embedding jobs are typically background pipelines — a swallowed submission error leaves the pipeline silently idle while downstream consumers wait for embeddings that will never arrive. try { const job = await ai.batches.createEmbeddings({ model: 'text-embedding-004', src: { fileName: 'files/my-input' }, }); // Poll ai.batches.get({ name: job.name }) until terminal state } catch (error) { if (error instanceof ApiError) { if (error.status === 429) throw new Error('Batch rate limited'); } throw error; }costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilent
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
Official documentation
- [7]ai.google.dev/gemini-api/docs/filesFiles
- [10]ai.google.dev/gemini-api/docs/cachingCaching
- [13]ai.google.dev/gemini-api/docs/file-searchFile Search
- [14]google.aip.dev/151151
- [16]ai.google.dev/gemini-api/docs/model-tuningModel Tuning
- [18]ai.google.dev/gemini-api/docs/batch-modeBatch Mode
Source code
- [1]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · README.md
- [2]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · errors.ts
- [3]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · models.ts
- [4]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · chats.ts
- [5]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · index.cjs%20(lines%207127-7160)
- [6]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · index.cjs%20(lines%207182-7225)
- [8]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · index.cjs%20(lines%208563-8583)
- [9]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · index.cjs%20(lines%2018305-18375)
- [11]raw.githubusercontent.com/googleapis/js-genai/maingoogleapis/js-genai · index.cjs%20(lines%206568-6630)
- [12]raw.githubusercontent.com/googleapis/js-genai/v2.10.0googleapis/js-genai · file_search_stores.ts
- [15]raw.githubusercontent.com/googleapis/js-genai/v2.10.0googleapis/js-genai · tunings.ts
- [17]raw.githubusercontent.com/googleapis/js-genai/v2.10.0googleapis/js-genai · batches.ts
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Sources — @google/genai
Fetched URLs (2026-04-02)
| URL | Summary |
|---|---|
| https://raw.githubusercontent.com/googleapis/js-genai/main/README.md | Main README with error handling examples showing ApiError class |
| https://raw.githubusercontent.com/googleapis/js-genai/main/src/errors.ts | Source for ApiError class — extends Error, has status and message properties |
| https://raw.githubusercontent.com/googleapis/js-genai/main/src/models.ts | Source for all model methods — generateContent, generateContentStream, generateImages, embedContent |
| https://ai.google.dev/api/generate-content | Gemini API generate content reference |
Key Evidence
ApiErroris the single error type thrown by allai.models.*async methodsApiError.statusexposes HTTP status code (401, 403, 429, 400, 500, 503)- SDK README explicitly shows try-catch pattern for generateContent
- Real-world usage in cline/cline (59k stars) shows proper ApiError handling with 429 detection
- Real-world antipattern in CherryHQ/cherry-studio-app: embedContent and models.list() called without try-catch
Need a different package?
Request a profile