-
Notifications
You must be signed in to change notification settings - Fork 2.7k
feat: Add "Handling Errors and Retries" quickstart notebook (Resolves #469) #1160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3864d77
a97a0c2
af553ae
e9d6854
7aca4a9
9c43272
34baf3d
a7df3f0
a1ee8de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,294 @@ | ||
| { | ||
|
kkorpal marked this conversation as resolved.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line #1. %pip install -U -q 'google-genai>=1.0.0' I don't think retry option existed in 1.0.0. Can you add the version the parameter was added instead? Reply via ReviewNB
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked the SDK history and it was added in 1.21.1. Can you use that as the minimum version and add a comment to say it's when retry was introduced?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kkorpal you missed this request
kkorpal marked this conversation as resolved.
kkorpal marked this conversation as resolved.
|
||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": { | ||
| "id": "3c5dbcc9ae0c" | ||
| }, | ||
| "source": [ | ||
| "##### Copyright 2025 Google LLC." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 1, | ||
| "id": "e426989b", | ||
| "metadata": { | ||
| "cellView": "form", | ||
| "id": "ffabf965472d" | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n", | ||
| "# you may not use this file except in compliance with the License.\n", | ||
| "# You may obtain a copy of the License at\n", | ||
| "#\n", | ||
| "# https://www.apache.org/licenses/LICENSE-2.0\n", | ||
| "#\n", | ||
| "# Unless required by applicable law or agreed to in writing, software\n", | ||
| "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", | ||
| "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", | ||
| "# See the License for the specific language governing permissions and\n", | ||
| "# limitations under the License." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": { | ||
| "id": "57c4e9f53bb1" | ||
| }, | ||
| "source": [ | ||
| "# Handling Errors and Retries with the Gemini API\n", | ||
| "\n", | ||
| "<a target=\"_blank\" href=\"https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/Handling_Errors_and_Retries.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" height=30/></a>" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "9a9a229b", | ||
| "metadata": { | ||
| "id": "b4fb8a01d330" | ||
| }, | ||
| "source": [ | ||
| "When building applications with the Gemini API, requests can occasionally fail. Understanding *why* a request failed is crucial because it dictates how your code should react. \n", | ||
| "\n", | ||
| "For a full list of API errors, review the [Gemini API Troubleshooting Documentation](https://ai.google.dev/gemini-api/docs/troubleshooting).\n", | ||
| "\n", | ||
| "## Error Types: When to Retry vs. When to Stop\n", | ||
| "\n", | ||
| "Not all errors should trigger a retry. If your prompt contains invalid parameters, retrying the exact same request will result in the exact same error, wasting time and resources. \n", | ||
| "\n", | ||
| "**Errors you SHOULD retry (Transient Errors):**\n", | ||
| "* **`429` (Resource Exhausted):** You have exceeded your rate limit or quota. *Action:* Pause and retry using exponential backoff.\n", | ||
| "* **`500` (Internal Error) & `503` (Service Unavailable):** The model is temporarily overloaded or experiencing a glitch. *Action:* Pause and retry.\n", | ||
| "* **`502` (Bad Gateway) & `504` (Gateway Timeout):** These are temporary network errors that occur between the client and the Google API frontend. Because these are usually caused by transient congestion or connection blips, they often resolve themselves in a matter of seconds. Action: Pause and retry.\n", | ||
| "\n", | ||
| "**Errors you SHOULD NOT retry (Client Errors):**\n", | ||
| "* **`400` (Bad Request):** Invalid parameters, unsupported model, or a prompt that is too long. *Action:* Stop. Fix the code or prompt before sending again.\n", | ||
| "* **`403` (Forbidden):** Invalid API key or permission denied. *Action:* Stop. Check your credentials.\n", | ||
| "* **`404` (Not Found):** The requested resource (e.g., a specific model name, a file in the File API, or a deleted cache) could not be found. Action: Stop. Verify the model name or resource ID." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "016ad8cb", | ||
| "metadata": { | ||
| "id": "61bd8ec2ebd8" | ||
| }, | ||
| "source": [ | ||
| "<a name=\"setup\"></a>\n", | ||
| "## Setup" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "dbb75afe", | ||
| "metadata": { | ||
| "id": "3eeaea0c235c" | ||
| }, | ||
| "source": [ | ||
| "### Install SDK\n", | ||
| "\n", | ||
| "Install the SDK from [PyPI](https://github.com/googleapis/python-genai). It's recommended to always use the latest version." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "1ae64aba", | ||
| "metadata": { | ||
| "id": "78bd0a6c0973" | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "%pip install -U -q 'google-genai>=1.51.0'" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "4af1143b", | ||
| "metadata": { | ||
| "id": "58994e6dce7a" | ||
| }, | ||
| "source": [ | ||
| "### Setup your API key\n", | ||
| "\n", | ||
| "To run the following cell, your API key must be stored in a Colab Secret named `GEMINI_API_KEY`. If you don't already have an API key, or you're not sure how to create a Colab Secret, see [Authentication](./Authentication.ipynb) for an example." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "978668a1", | ||
| "metadata": { | ||
| "id": "f591a97f76c3" | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from google.colab import userdata\n", | ||
| "\n", | ||
| "GEMINI_API_KEY = userdata.get('GEMINI_API_KEY')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "eebd4846", | ||
| "metadata": { | ||
| "id": "fdd10cf335cd" | ||
| }, | ||
| "source": [ | ||
| "### Choose a model\n", | ||
| "\n", | ||
| "Select the model you want to use in this guide. You can either select one from the list or enter a model name manually. Keep in mind that some models, such as the 2.5 ones are thinking models and thus take slightly more time to respond. For more details, you can see [thinking notebook ](./Get_started_thinking.ipynb) to learn how to control the thinking.\n", | ||
| "\n", | ||
| "Feel free to select [Gemini 3 Pro](https://ai.google.dev/gemini-api/docs/models#gemini-3-pro) if you want to try our newest model, but keep in mind that it has no free tier.\n", | ||
| "\n", | ||
| "For a full overview of all Gemini models, check the [documentation](https://ai.google.dev/gemini-api/docs/models/gemini)." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "a5665419", | ||
| "metadata": { | ||
| "id": "1d8e3d32b024" | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "MODEL_ID = \"gemini-3-flash-preview\" # @param [\"gemini-2.5-flash-lite\", \"gemini-3.1-flash-lite-preview\", \"gemini-2.5-flash\", \"gemini-3-flash-preview\", \"gemini-2.5-pro\", \"gemini-3.1-pro-preview\"] {\"allow-input\":true, \"isTemplate\": true}" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "ebcf2ed7", | ||
| "metadata": { | ||
| "id": "fb06b64b8c96" | ||
| }, | ||
| "source": [ | ||
| "## Method 1: Use Built-in SDK HttpRetryOptions\n", | ||
| "\n", | ||
| "For standard use cases, the `google-genai` SDK handles retries automatically. Configure the `HttpRetryOptions` when initializing the client. The SDK will automatically catch `429` and `5xx` errors and apply exponential backoff under the hood." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "7aff7f2c", | ||
| "metadata": { | ||
| "id": "f1c25d1b6186" | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from google import genai\n", | ||
| "from google.genai import types\n", | ||
| "\n", | ||
| "client = genai.Client(\n", | ||
| " api_key=GEMINI_API_KEY,\n", | ||
| " http_options=types.HttpOptions(\n", | ||
| " retry_options=types.HttpRetryOptions(\n", | ||
| " attempts=5, \n", | ||
| " initial_delay=2.0, \n", | ||
| " max_delay=60.0, \n", | ||
| " http_status_codes=[429, 500, 502, 503, 504] \n", | ||
| " )\n", | ||
| " )\n", | ||
| ")\n", | ||
| "\n", | ||
| "response = client.models.generate_content(\n", | ||
| " model=MODEL_ID,\n", | ||
| " contents='Explain exponential backoff in one sentence.'\n", | ||
| ")\n", | ||
| "print(response.text)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": { | ||
| "id": "867006d54746" | ||
| }, | ||
| "source": [ | ||
| "## Method 2: Custom Error Handling and Backoff\n", | ||
| "\n", | ||
| "For complex applications, you may want precise control over the retry logic. The following example demonstrates how to catch the `errors.APIError` exception, inspect the specific HTTP status code, and execute a custom exponential backoff loop based on the error type." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": { | ||
| "id": "35d2402cdb4c" | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from google import genai\n", | ||
| "from google.genai import errors\n", | ||
| "import time\n", | ||
| "\n", | ||
| "# Re-initialize a standard client without automatic retries\n", | ||
| "client = genai.Client(api_key=GEMINI_API_KEY)\n", | ||
| "\n", | ||
| "max_retries = 4\n", | ||
| "base_delay = 2.0\n", | ||
| "\n", | ||
| "for attempt in range(max_retries):\n", | ||
| " try:\n", | ||
| " response = client.models.generate_content(\n", | ||
| " model=MODEL_ID,\n", | ||
| " contents='Write a short poem about resilience.'\n", | ||
| " )\n", | ||
| " print(\"Success!\\n\")\n", | ||
| " print(response.text)\n", | ||
| " break # Break out of the loop if successful\n", | ||
| "\n", | ||
| " except errors.APIError as e:\n", | ||
| " print(f\"Attempt {attempt + 1} failed. Error Code: {e.code} - {e.message}\")\n", | ||
| " \n", | ||
| " # Check if it is a transient error that can be retried\n", | ||
| " if e.code in [429, 500, 502, 503, 504]:\n", | ||
| " delay = base_delay * (2 ** attempt) # Exponential backoff (2s, 4s, 8s...)\n", | ||
| " print(f\"Transient error detected. Retrying in {delay} seconds...\\n\")\n", | ||
| " time.sleep(delay)\n", | ||
| " \n", | ||
| " # If it is a client error (e.g., 400 Bad Request), retrying won't help!\n", | ||
| " elif e.code in [400, 403, 404]:\n", | ||
| " print(\"Fatal client error detected. Please fix your request parameters. Stopping retries.\")\n", | ||
| " break\n", | ||
| " \n", | ||
| " else:\n", | ||
| " print(\"Unexpected error encountered. Stopping retries.\")\n", | ||
| " break\n", | ||
| "else:\n", | ||
| " print(\"Maximum retries reached. The request ultimately failed.\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": { | ||
| "id": "cdd9de6428f6" | ||
| }, | ||
| "source": [ | ||
| "## Next steps\n", | ||
| "\n", | ||
| "<a name=\"next_steps\"></a>\n", | ||
| "\n", | ||
| "* [Prompting Strategies](./Prompting.ipynb): Learn how to craft better prompts to minimize errors and improve response quality.\n", | ||
| "* [Functional Calling](./Function_calling.ipynb): Teach the model how to use external tools and APIs when it encounters tasks it cannot solve alone.\n", | ||
| "* [Context Caching](./Caching.ipynb): Learn how to reduce costs and latency for large prompts by caching frequently used data.\n", | ||
| "\n", | ||
| "Also check the other Gemini capabilities that you can find in the [Gemini quickstarts](https://github.com/google-gemini/cookbook/tree/main/quickstarts/)." | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "colab": { | ||
| "name": "Handling_Errors_and_Retries.ipynb", | ||
| "toc_visible": true | ||
| }, | ||
| "kernelspec": { | ||
| "display_name": "Python 3", | ||
| "name": "python3" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 0 | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved after "Configure model parameters" as this is not a Gemini 3 specific feature
Reply via ReviewNB
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kkorpal I think you missed this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kkorpal And this one