From 0df61ac9770d6f544ff7356cd2e8bb2a73c4cdd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Fri, 26 Jun 2026 15:22:15 +0200 Subject: [PATCH 1/5] Add Troubleshooting Gemini API keys section Document why a bad Gemini API key fails silently: createClient does no network call, so auth/access failures surface only via the Live session onerror/onclose callbacks. Covers common causes (wrong/mistyped key, wrong project, API not enabled, region block, native-audio 1008 'leaked'/1011 model-specific rejection) and how to fix. --- docs/integrations/gemini-live-integration.mdx | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/integrations/gemini-live-integration.mdx b/docs/integrations/gemini-live-integration.mdx index bc0f138f..16f456c2 100644 --- a/docs/integrations/gemini-live-integration.mdx +++ b/docs/integrations/gemini-live-integration.mdx @@ -152,7 +152,7 @@ Create a Fishjam agent configured to match the audio format that the Google clie ### Step 3: Connect the Streams -:::warning Encoding +:::warning[Encoding] Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. Ensure you convert between them correctly as shown below. ::: @@ -278,3 +278,50 @@ Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. + +## Troubleshooting Gemini API keys + +If your agent joins the room but silently does nothing — no audio, no transcription, and no error — the problem is almost always the Gemini API key. + +:::warning Errors are asynchronous, not thrown +`createClient` is a thin synchronous wrapper around Google's `GoogleGenAI` and makes no network call, so an invalid or unauthorized key is **not** rejected when you create the client. The key is only exercised later, when `live.connect()` opens the Live websocket. Authentication and access failures then surface through the Live session callbacks (`onerror` / `onclose`) — never as a thrown exception. +::: + +Always implement the `onerror` and `onclose` callbacks and log the close code and reason. That is where the real error message appears: + +```ts +import GeminiIntegration from "@fishjam-cloud/js-server-sdk/gemini"; +import { Modality, type LiveServerMessage } from "@google/genai"; + +const genAi = GeminiIntegration.createClient({ + apiKey: process.env.GOOGLE_API_KEY!, +}); +const GEMINI_MODEL = "gemini-2.5-flash-native-audio-preview-12-2025"; +const handleMessage = (msg: LiveServerMessage) => {}; + +// ---cut--- +const session = await genAi.live.connect({ + model: GEMINI_MODEL, + config: { responseModalities: [Modality.AUDIO] }, + callbacks: { + onopen: () => console.log("Gemini Live connected"), + onerror: (e) => console.error("Gemini Live error:", e), + onclose: (e) => + console.error(`Gemini Live closed: code=${e.code} reason=${e.reason}`), + onmessage: handleMessage, + }, +}); +``` + +### Common reasons a key doesn't work + +- Wrong or mistyped key, or a key from the wrong Google Cloud project. +- The Gemini API is not enabled for the key's project. +- Region or country restriction — the Gemini API (or its free tier) is not available in your region. +- Model-specific rejection. The native-audio Live models (e.g. `gemini-2.5-flash-native-audio-preview-*`) can reject an otherwise-valid key: the websocket closes with code `1008` and the message _"Your API key was reported as leaked. Please use another API key."_ (even when the key is not actually leaked), or with code `1011` internal errors. A key that works for other models can still fail here. + +### How to fix + +- Verify or regenerate your key at [Google AI Studio](https://aistudio.google.com/app/apikey). +- Confirm the Gemini API is enabled for the key's project and that your region is supported. +- If you see the `1008` "leaked" message, rotate to a freshly generated key. From 46e67ab02b4a4db68ca87f7882edf0b357ee5858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Fri, 26 Jun 2026 15:55:51 +0200 Subject: [PATCH 2/5] Fix Encoding admonition title in versioned Gemini docs --- .../version-0.26.0/integrations/gemini-live-integration.mdx | 2 +- .../version-0.27.0/integrations/gemini-live-integration.mdx | 2 +- .../version-0.28.0/integrations/gemini-live-integration.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/versioned_docs/version-0.26.0/integrations/gemini-live-integration.mdx b/versioned_docs/version-0.26.0/integrations/gemini-live-integration.mdx index 33518a1e..139b6533 100644 --- a/versioned_docs/version-0.26.0/integrations/gemini-live-integration.mdx +++ b/versioned_docs/version-0.26.0/integrations/gemini-live-integration.mdx @@ -152,7 +152,7 @@ Create a Fishjam agent configured to match the audio format that the Google clie ### Step 3: Connect the Streams -:::warning Encoding +:::warning[Encoding] Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. Ensure you convert between them correctly as shown below. ::: diff --git a/versioned_docs/version-0.27.0/integrations/gemini-live-integration.mdx b/versioned_docs/version-0.27.0/integrations/gemini-live-integration.mdx index 33518a1e..139b6533 100644 --- a/versioned_docs/version-0.27.0/integrations/gemini-live-integration.mdx +++ b/versioned_docs/version-0.27.0/integrations/gemini-live-integration.mdx @@ -152,7 +152,7 @@ Create a Fishjam agent configured to match the audio format that the Google clie ### Step 3: Connect the Streams -:::warning Encoding +:::warning[Encoding] Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. Ensure you convert between them correctly as shown below. ::: diff --git a/versioned_docs/version-0.28.0/integrations/gemini-live-integration.mdx b/versioned_docs/version-0.28.0/integrations/gemini-live-integration.mdx index bc0f138f..43a0eeca 100644 --- a/versioned_docs/version-0.28.0/integrations/gemini-live-integration.mdx +++ b/versioned_docs/version-0.28.0/integrations/gemini-live-integration.mdx @@ -152,7 +152,7 @@ Create a Fishjam agent configured to match the audio format that the Google clie ### Step 3: Connect the Streams -:::warning Encoding +:::warning[Encoding] Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. Ensure you convert between them correctly as shown below. ::: From 76c20184c0210e9debf134ee25bc3b28130b71b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Fri, 26 Jun 2026 16:09:45 +0200 Subject: [PATCH 3/5] Fix danger/info admonition titles to bracket syntax --- docs/_common/agents/remember-to-disconnect.mdx | 2 +- .../version-0.26.0/_common/agents/remember-to-disconnect.mdx | 2 +- versioned_docs/version-0.26.0/tutorials/livestreaming.mdx | 2 +- .../version-0.27.0/_common/agents/remember-to-disconnect.mdx | 2 +- .../version-0.28.0/_common/agents/remember-to-disconnect.mdx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/_common/agents/remember-to-disconnect.mdx b/docs/_common/agents/remember-to-disconnect.mdx index 4960eed1..d1a45cd5 100644 --- a/docs/_common/agents/remember-to-disconnect.mdx +++ b/docs/_common/agents/remember-to-disconnect.mdx @@ -1,3 +1,3 @@ -:::danger Remember to disconnect your agents! +:::danger[Remember to disconnect your agents!] It's important to disconnect agents, because **every connected agent generates usage** just as a normal peer. ::: diff --git a/versioned_docs/version-0.26.0/_common/agents/remember-to-disconnect.mdx b/versioned_docs/version-0.26.0/_common/agents/remember-to-disconnect.mdx index 4960eed1..d1a45cd5 100644 --- a/versioned_docs/version-0.26.0/_common/agents/remember-to-disconnect.mdx +++ b/versioned_docs/version-0.26.0/_common/agents/remember-to-disconnect.mdx @@ -1,3 +1,3 @@ -:::danger Remember to disconnect your agents! +:::danger[Remember to disconnect your agents!] It's important to disconnect agents, because **every connected agent generates usage** just as a normal peer. ::: diff --git a/versioned_docs/version-0.26.0/tutorials/livestreaming.mdx b/versioned_docs/version-0.26.0/tutorials/livestreaming.mdx index a160ea3e..b00a9388 100644 --- a/versioned_docs/version-0.26.0/tutorials/livestreaming.mdx +++ b/versioned_docs/version-0.26.0/tutorials/livestreaming.mdx @@ -21,7 +21,7 @@ In this tutorial we explain how to publish and view streams with the client SDKs If you prefer to use WHIP or WHEP directly, then you should read [WHIP/WHEP with Fishjam](../how-to/backend/whip-whep). ::: -:::info Cost-effective audio livestreams +:::info[Cost-effective audio livestreams] Fishjam supports audio-only livestreams at a reduced cost of $0.20 per 1000 minutes of the streamer and each listener (compared to $0.80 for video livestreams). This is ideal for podcasts, radio-style broadcasts, or any scenario where video isn't necessary. See [Audio-only calls](../how-to/backend/audio-only-calls) for more information. diff --git a/versioned_docs/version-0.27.0/_common/agents/remember-to-disconnect.mdx b/versioned_docs/version-0.27.0/_common/agents/remember-to-disconnect.mdx index 4960eed1..d1a45cd5 100644 --- a/versioned_docs/version-0.27.0/_common/agents/remember-to-disconnect.mdx +++ b/versioned_docs/version-0.27.0/_common/agents/remember-to-disconnect.mdx @@ -1,3 +1,3 @@ -:::danger Remember to disconnect your agents! +:::danger[Remember to disconnect your agents!] It's important to disconnect agents, because **every connected agent generates usage** just as a normal peer. ::: diff --git a/versioned_docs/version-0.28.0/_common/agents/remember-to-disconnect.mdx b/versioned_docs/version-0.28.0/_common/agents/remember-to-disconnect.mdx index 4960eed1..d1a45cd5 100644 --- a/versioned_docs/version-0.28.0/_common/agents/remember-to-disconnect.mdx +++ b/versioned_docs/version-0.28.0/_common/agents/remember-to-disconnect.mdx @@ -1,3 +1,3 @@ -:::danger Remember to disconnect your agents! +:::danger[Remember to disconnect your agents!] It's important to disconnect agents, because **every connected agent generates usage** just as a normal peer. ::: From ef2fd70aad04cdf53ead28ffeffb1232ac400bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Fri, 26 Jun 2026 16:10:59 +0200 Subject: [PATCH 4/5] Fix Errors admonition title to bracket syntax --- docs/integrations/gemini-live-integration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/gemini-live-integration.mdx b/docs/integrations/gemini-live-integration.mdx index 16f456c2..16e20f8d 100644 --- a/docs/integrations/gemini-live-integration.mdx +++ b/docs/integrations/gemini-live-integration.mdx @@ -283,7 +283,7 @@ Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. If your agent joins the room but silently does nothing — no audio, no transcription, and no error — the problem is almost always the Gemini API key. -:::warning Errors are asynchronous, not thrown +:::warning[Errors are asynchronous, not thrown] `createClient` is a thin synchronous wrapper around Google's `GoogleGenAI` and makes no network call, so an invalid or unauthorized key is **not** rejected when you create the client. The key is only exercised later, when `live.connect()` opens the Live websocket. Authentication and access failures then surface through the Live session callbacks (`onerror` / `onclose`) — never as a thrown exception. ::: From 416a21d90ca42d814605165085abf6f2c5714e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Fri, 26 Jun 2026 17:14:40 +0200 Subject: [PATCH 5/5] Scope Gemini key-error admonition: auth fails via callbacks, connect() can still reject (Copilot) --- docs/integrations/gemini-live-integration.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/integrations/gemini-live-integration.mdx b/docs/integrations/gemini-live-integration.mdx index 16e20f8d..a871db1e 100644 --- a/docs/integrations/gemini-live-integration.mdx +++ b/docs/integrations/gemini-live-integration.mdx @@ -283,8 +283,8 @@ Fishjam handles raw bytes, while Google GenAI SDKs often expect Base64 strings. If your agent joins the room but silently does nothing — no audio, no transcription, and no error — the problem is almost always the Gemini API key. -:::warning[Errors are asynchronous, not thrown] -`createClient` is a thin synchronous wrapper around Google's `GoogleGenAI` and makes no network call, so an invalid or unauthorized key is **not** rejected when you create the client. The key is only exercised later, when `live.connect()` opens the Live websocket. Authentication and access failures then surface through the Live session callbacks (`onerror` / `onclose`) — never as a thrown exception. +:::warning[API key errors surface asynchronously] +`createClient` is a thin synchronous wrapper around Google's `GoogleGenAI` and makes no network call, so an invalid or unauthorized key is **not** rejected when you create the client. The key is only exercised later, when `live.connect()` opens the Live websocket. Authentication and access failures then typically surface through the Live session callbacks (`onerror` / `onclose`) rather than as a thrown exception. (Other problems, such as a malformed config, can still reject the awaited `connect()` call — keep a `try/catch` around it too.) ::: Always implement the `onerror` and `onclose` callbacks and log the close code and reason. That is where the real error message appears: