Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 286 additions & 0 deletions quickstarts-js/Error_handling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
/*
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* Markdown (render)
# Gemini API: Error handling
*/

/* Markdown (render)
This notebook demonstrates strategies for handling common errors you might encounter when working with the Gemini API:

* **Transient Errors:** Temporary failures due to network issues, server overload, etc.
* **Rate Limits:** Restrictions on the number of requests you can make within a certain timeframe.
* **Timeouts:** When an API call takes too long to complete.

You have two main approaches to explore:

1. **Automatic retries:** A simple way to retry requests when they fail due to transient errors.
2. **Manual backoff and retry:** A more customizable approach that provides finer control over retry behavior.


**Gemini Rate Limits**

The default rate limits for different Gemini models are outlined in the [Gemini API model documentation](https://ai.google.dev/gemini-api/docs/models/gemini#model-variations). If your application requires a higher quota, consider [requesting a rate limit increase](https://ai.google.dev/gemini-api/docs/quota).
*/

/* Markdown (render)
### API Key Configuration
To ensure security, avoid hardcoding the API key in frontend code. Instead, set it as an environment variable on the server or local machine.
When using the Gemini API client libraries, the key will be automatically detected if set as either `GEMINI_API_KEY` or `GOOGLE_API_KEY`. If both are set, `GOOGLE_API_KEY` takes precedence.
For instructions on setting environment variables, see the official documentation: [Set API Key as Environment Variable](https://ai.google.dev/gemini-api/docs/api-key#set-api-env-var)
*/

// [CODE STARTS]
module = await import("https://esm.sh/@google/[email protected]");
GoogleGenAI = module.GoogleGenAI;
ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
Comment on lines +47 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Variables module, GoogleGenAI, and ai are being assigned without declaration (const, let, or var). This creates global variables, which is a bad practice in JavaScript. Please declare them using const since they are not reassigned.

const module = await import("https://esm.sh/@google/[email protected]");
const GoogleGenAI = module.GoogleGenAI;
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

// [CODE ENDS]

/* Markdown (render)
### Automatic retries

The Gemini API's client library offers built-in retry mechanisms for handling transient errors. You can enable this feature by using the `request_options` argument with API calls like `generate_content`, `generate_answer`, `embed_content`, and `generate_content_async`.

**Advantages:**

* **Simplicity:** Requires minimal code changes for significant reliability gains.
* **Robust:** Effectively addresses most transient errors without additional logic.

**Customize retry behavior:**

Use these settings in [`retry`](https://googleapis.dev/python/google-api-core/latest/retry.html) to customize retry behavior:

* `predicate`: (callable) Determines if an exception is retryable. Default: [`if_transient_error`](https://github.com/googleapis/python-api-core/blob/main/google/api_core/retry/retry_base.py#L75C4-L75C13)
* `initial`: (float) Initial delay in seconds before the first retry. Default: `1.0`
* `maximum`: (float) Maximum delay in seconds between retries. Default: `60.0`
* `multiplier`: (float) Factor by which the delay increases after each retry. Default: `2.0`
* `timeout`: (float) Total retry duration in seconds. Default: `120.0`
*/
Comment on lines +53 to +71
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This documentation section about automatic retries appears to be copied from a Python notebook and is incorrect for the JavaScript SDK.

  • The request_options argument for retry configuration is a feature of the Python SDK.
  • The link on line 64 points to the Python google-api-core library.
  • The retry parameters listed (predicate, initial, etc.) are for the Python library.

This is misleading for JavaScript developers. Please update this section to reflect the capabilities of the @google/genai JavaScript SDK.


// [CODE STARTS]
MODEL_ID = "gemini-2.5-flash";
prompt = "Write a story about a magic backpack.";
Comment on lines +74 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Variables MODEL_ID and prompt are used without declaration. Please declare them with const.

const MODEL_ID = "gemini-2.5-flash";
const prompt = "Write a story about a magic backpack.";


response = await ai.models.generateContent({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The response variable is used without declaration. Please declare it with const.

const response = await ai.models.generateContent({

model: MODEL_ID,
contents: prompt,
});

console.log(response.text);
// [CODE ENDS]

/* Output Sample
Elara wasn’t looking for magic. She was looking for a backpack. Her old one, affectionately nicknamed “The Beast,” had finally given up the ghost, its seams ripped and its zipper permanently jammed. So, she found herself in Mrs. Willowby’s Oddity Emporium, a place smelling of mothballs and forgotten dreams.

The backpack in question was tucked away in a dusty corner, almost hidden behind a taxidermied two-headed duck. It was made of a deep indigo fabric, embroidered with silver constellations that shimmered faintly even in the dim light. It was perfect.

“That one’s been here for ages,” Mrs. Willowby croaked, dusting it off with a flourish. “Nobody seems to want it.”

Elara didn't care. She paid the paltry sum, slung the backpack over her shoulder, and hurried home.

The first sign that something was amiss came the next day. Packing for school, Elara discovered the backpack was inexplicably larger inside than out. She could fit her textbooks, lunch, a bulky art project, and still have room for more. It was like a miniature, indigo TARDIS.

Then came the apple. She’d absentmindedly tossed an apple into the backpack, then spent the next five minutes searching for it. When she finally gave up, she pulled out her history book – and the apple was perched perfectly on top, gleaming as if freshly polished.

Over the next few weeks, Elara discovered the backpack’s magic was more whimsical than powerful. It couldn't grant wishes or transport her to other dimensions, but it could certainly make life interesting. It could produce the exact right color of paint she needed for her art project, always perfectly blended. It could conjure a warm scarf on a chilly day. It could even, on one particularly stressful day, produce a miniature, purring kitten that promptly curled up in her lap.

The backpack seemed to respond to Elara’s needs, often anticipating them. If she was bored, it would produce a book she'd been meaning to read. If she was nervous about a test, it would contain a perfectly sharpened pencil and a reassuring note, scrawled in elegant script she didn't recognize.

But the magic wasn't always predictable. One day, she reached in for her math textbook and pulled out a handful of sand, complete with a tiny, brightly colored seashell. Another time, expecting her lunch, she found a single, perfectly ripe strawberry.

Elara kept the backpack's magic a secret. It was her little secret, her personal quirk in a world that often felt mundane. She loved the element of surprise, the anticipation of what the backpack would conjure next.

One day, however, she overheard a girl in her class, Maya, crying in the hallway. Maya had lost her grandmother's locket, a tiny silver heart that was her most treasured possession. Elara hesitated. Could the backpack help?

She found Maya sitting on a bench, tears streaming down her face. "Maya," she said softly, "I... I might be able to help."

Reluctantly, Elara explained about the backpack. Maya looked at her with a mixture of disbelief and hope. Elara unzipped the backpack, her heart pounding. She closed her eyes, pictured the locket, the delicate silver chain, the intricate heart shape. She reached inside.

Her fingers brushed against something cold and smooth. She pulled it out. It was the locket.

Maya gasped, her eyes wide with wonder. "That's it! That's my locket!" She snatched it from Elara's hand and clutched it to her chest. "Thank you," she whispered, tears still falling, but now tears of joy.

From that day on, Elara didn't keep the backpack's magic a secret. She didn't advertise it, but when someone needed help, she offered what she could. A lost homework assignment, a forgotten umbrella, a comforting word – the backpack seemed to know exactly what was needed.

The indigo backpack wasn't just a bag; it was a symbol of hope, a reminder that even in the most ordinary of lives, a little bit of magic could make all the difference. And Elara, the girl who had simply been looking for a replacement for "The Beast," had become something much more – a bearer of magic, and a friend to those who needed it most. And she knew, with a certainty that warmed her from the inside out, that the magic of the backpack was only just beginning.
*/

/* Markdown (render)
### Manually increase timeout when responses take time

If you encounter `TimeoutError` or similar errors, meaning an API call exceeds the default timeout, you can manually adjust it by defining `timeout` in the `requestOptions` argument.
*/

// [CODE STARTS]
prompt = "Write a story about a magic backpack.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The prompt variable is used without declaration. Please declare it with const.

const prompt = "Write a story about a magic backpack.";


// Increase timeout to 15 minutes (900000 milliseconds)
const requestOptions = {
timeout: 900000,
};

const response = await ai.models.generateContent({
model: MODEL_ID,
contents: prompt,
requestOptions: requestOptions,
});
Comment on lines +135 to +139
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The requestOptions object is being passed inside the generateContent request body. This is not the correct way to specify request options like timeout for the @google/genai SDK, and this option will likely be ignored. To set a timeout, you should either create a new GoogleGenAI client with the desired requestOptions or use ai.getGenerativeModel() to get a model instance with custom options.

const modelWithTimeout = ai.getGenerativeModel({ model: MODEL_ID }, requestOptions);
const response = await modelWithTimeout.generateContent(prompt);


console.log(response);
// [CODE ENDS]

/* Output Sample
{
"candidates": [
{
"content": {
"parts": [
{
"text": "Flora had always been unremarkable. Brown hair, brown eyes, perpetually lost in a book. Even her backpack, a drab canvas thing she'd inherited from her older brother, screamed \"invisible.\" Until, one Tuesday morning, it didn't.\n\nShe was rushing to catch the bus, her fingers fumbling with the zipper of the aforementioned backpack, when it refused to budge. Frustrated, she yanked harder. There was a ripping sound, but instead of canvas tearing, a shimmering, iridescent light spilled out.\n\nFlora gasped. The inside of the backpack wasn't canvas anymore. It was… a swirling vortex of colours, like the aurora borealis compressed into a small space. Hesitantly, she reached in. Her fingers brushed against something soft, and she pulled it out.\n\nIt was a perfect, crimson apple, polished to a gleam. She hadn't packed an apple. She hadn't packed anything, actually, other than a crumpled textbook and a half-eaten bag of chips. She shrugged and took a bite.\n\nThe apple exploded in her mouth with a flavour she'd never experienced. It tasted of sunshine, laughter, and the comforting smell of rain on dry earth. She felt a surge of energy, of boundless possibility.\n\nFrom that day on, Flora's life was anything but unremarkable. The backpack, it turned out, was magical. Whatever she needed, the backpack provided. Not necessarily what she *wanted*, but what she *needed*.\n\nOne day, facing a daunting math test, she reached in, hoping for a cheat sheet. Instead, she pulled out a skipping rope. Confused, she tucked it into her pocket. During the test, she felt a rising tide of anxiety. On impulse, she pulled out the rope and, ignoring the bewildered stares of her classmates, started skipping in the corner. The rhythm calmed her, cleared her head, and suddenly, the formulas clicked into place. She aced the test.\n\nAnother time, feeling lonely and ignored, she hoped for a new friend. The backpack gave her a packet of wildflower seeds. Disappointed, she almost tossed them aside. But then she remembered the empty patch of dirt behind the school. She planted the seeds, nurtured them, and soon, a riot of colours bloomed. Students, drawn to the vibrant flowers, started talking to her, helping her tend the garden. She found her community.\n\nThe backpack wasn't always easy. Sometimes, it gave her things she didn't understand or appreciate at first. A rusty key, a chipped teacup, a single, perfect feather. But each time, in its own strange way, the object taught her a lesson, filled a need she didn't even know she had.\n\nOne day, rummaging through the backpack for a pen, Flora found something unexpected: a small, leather-bound book. Its pages were blank, but the title was embossed in gold: \"The Unwritten Story.\"\n\nShe understood. The backpack wasn't just providing her with objects; it was providing her with opportunities, with the raw materials to build her own extraordinary life. It was up to her to write the story.\n\nFlora closed the book, a smile playing on her lips. She still had brown hair and brown eyes, and she still loved to read. But now, she carried herself with a newfound confidence, a spark of adventure in her eyes. She was no longer invisible. The magic backpack had helped her discover the magic within herself. And the greatest magic of all, she realised, was the power to create her own destiny, one chapter, one challenge, one adventure at a time."
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0,
"safetyRatings": []
}
],
"usageMetadata": {
"promptTokenCount": 8,
"candidatesTokenCount": 752,
"totalTokenCount": 760
}
}
*/

/* Markdown (render)
**Caution:** While increasing timeouts can be helpful, be mindful of setting them too high, as this can delay error detection and potentially waste resources.
*/

/* Markdown (render)
### Manually implement backoff and retry with error handling

For finer control over retry behavior and error handling, you can use the [`retry`](https://googleapis.dev/python/google-api-core/latest/retry.html) library (or similar libraries like [`backoff`](https://pypi.org/project/backoff/) and [`tenacity`](https://tenacity.readthedocs.io/en/latest/)). This gives you precise control over retry strategies and allows you to handle specific types of errors differently.
*/
Comment on lines +174 to +177
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The documentation for manual retry implementation links to Python libraries (retry, backoff, tenacity). This is a JavaScript file, and these links are irrelevant and confusing for JS developers. Please remove the links or replace them with links to equivalent JavaScript libraries.


// [CODE STARTS]
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function generateWithRetry(prompt) {
let retries = 3; // Max retries
let backoff = 1000; // Initial delay in ms

for (let i = 0; i < retries; i++) {
try {
const response = await ai.models.generateContent({
model: MODEL_ID,
contents: prompt,
});
return response;
} catch (e) {
console.log(`Attempt ${i + 1} failed. Retrying in ${backoff / 1000} seconds...`);
if (i < retries - 1) {
await delay(backoff);
backoff *= 2; // Exponential backoff
} else {
console.error("All retry attempts failed.", e);
throw e; // Re-throw the last error
}
}
}
}

prompt = "Write a one-liner advertisement for magic backpack.";
response = await generateWithRetry(prompt);
Comment on lines +206 to +207
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Variables prompt and response are used without declaration. Please declare them with const.

const prompt = "Write a one-liner advertisement for magic backpack.";
const response = await generateWithRetry(prompt);

console.log(response);
// [CODE ENDS]

/* Output Sample
{
"candidates": [
{
"content": {
"parts": [
{
"text": "Unzip endless possibilities with the Magic Backpack - more space, more adventure!\n"
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0,
"safetyRatings": []
}
],
"usageMetadata": {
"promptTokenCount": 10,
"candidatesTokenCount": 16,
"totalTokenCount": 26
}
}
*/

/* Markdown (render)
### Test the error handling with retry mechanism

To validate that your error handling and retry mechanism work as intended, you can simulate a failure. The following function is designed to fail on its first attempt and then succeed on the retry.
*/

// [CODE STARTS]
let callCounter = 0;

const delaySim = (ms) => new Promise(resolve => setTimeout(resolve, ms));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function delaySim is identical to the delay function defined on line 180. To avoid code duplication, please remove delaySim and use delay on line 267.


async function generateContentFirstFail(prompt) {
let retries = 3;
let backoff = 1000;

for (let i = 0; i < retries; i++) {
try {
callCounter++;
if (callCounter === 1) {
throw new Error("Simulated Service Unavailable");
}

const response = await ai.models.generateContent({
model: MODEL_ID,
contents: prompt,
});
return response.text;
} catch (e) {
console.log(`Error: ${e.message}`);
if (i < retries - 1) {
console.log(`Retrying in ${backoff / 1000} seconds...`);
await delaySim(backoff);
backoff *= 2;
} else {
console.error("All retry attempts failed.");
throw e;
}
}
}
}

prompt = "Write a one-liner advertisement for magic backpack.";
response = await generateContentFirstFail(prompt);
Comment on lines +277 to +278
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Variables prompt and response are used without declaration. Additionally, the generateContentFirstFail function relies on a global callCounter which is not reset, making the test non-idempotent. Please declare the variables and reset the counter before the call to ensure the test is reliable.

const prompt = "Write a one-liner advertisement for magic backpack.";
callCounter = 0; // Reset counter before test
const response = await generateContentFirstFail(prompt);

console.log(response);
// [CODE ENDS]

/* Output Sample
Error: Simulated Service Unavailable
Retrying in 1 seconds...
Unzip the impossible with the Magic Backpack - where adventure always fits!
*/
1 change: 1 addition & 0 deletions quickstarts-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Stay tuned, more JavaScript notebooks are on the way!
| --- | --- | --- | --- | --- |
| Get Started | A comprehensive introduction to the Gemini JS/TS SDK, demonstrating features such as text and multimodal prompting, token counting, system instructions, safety filters, multi-turn chat, output control, function calling, content streaming, file uploads, and using URL or YouTube video context. | Explore core Gemini capabilities in JS/TS | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/get_started?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Get_Started.js](./Get_Started.js) |
| Counting Tokens | Learn how tokens work in Gemini, how to count them, and how context windows affect requests. Includes text, image, and audio tokenization. | Token counting, context windows, multimodal tokens | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/counting_tokens?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Counting_Tokens.js](./Counting_Tokens.js) |
| Error Handling | Learn to handle common API errors like timeouts and rate limits in JavaScript, with examples on setting custom timeouts and implementing manual retry logic with exponential backoff. | Error handling, timeouts, retries, exponential backoff | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/error_handling?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Error_handling.js](./Error_handling.js) |
| Image Output | Generate and iterate on images using Gemini’s multimodal capabilities. Learn to use text+image responses, edit images mid-conversation, and handle multiple image outputs with chat-style prompting. | Image generation, multimodal output, image editing, iterative refinement | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/get_started_image_out?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [ImageOutput.js](./ImageOutput.js) |
| File API | Learn how to upload, use, retrieve, and delete files (text, image, audio, code) with the Gemini File API for multimodal prompts. | File upload, multimodal prompts, text/code/media files | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/file_api?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [File_API.js](./File_API.js) |
| Audio | Demonstrates how to use audio files with Gemini: upload, prompt, summarize, transcribe, and analyze audio and YouTube content. | Audio file upload, inline audio, transcription, YouTube analysis | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/audio?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Audio.js](./Audio.js) |
Expand Down