Skip to content

Revisit Interactions API requestUrl/CORS workaround once js-genai #1723 is fixed #1023

Description

@allenhutchison

Summary

The Interactions API transport (#1014, epic #1013) ships a workaround that routes the @google/genai Next-Gen / Interactions client through Obsidian's requestUrl instead of the renderer's global fetch. This is necessary because the Interactions client currently can't be called directly from a browser-like environment (Obsidian's Electron renderer) due to a CORS preflight failure. This issue tracks removing the workaround once the upstream bug is fixed.

Background: why the workaround exists

  • The Next-Gen client (behind client.interactions.*) adds an Api-Revision request header to every call.
  • In a browser/renderer, that custom header triggers a CORS preflight (OPTIONS). generativelanguage.googleapis.com does not list api-revision in its Access-Control-Allow-Headers, so the preflight returns 403 with no Access-Control-Allow-Origin and the real request never goes out. The plugin surfaces this as:
    [GeminiClient] Error creating interaction: TypeError: Failed to fetch
        at ...fetchWithTimeout ... Error: Connection error. at ...makeRequest
    
  • models.generateContent is unaffected (it never sets Api-Revision) — which is why the legacy transport works in Obsidian but the Interactions transport did not.
  • In Node there is no preflight, so the Interactions API works server-side. This is a browser-only bug.
  • The SDK exposes no public hook to inject a custom fetch into the Next-Gen client, so the workaround patches fetch onto the lazily-constructed client instance via the SDK-internal getNextGenClient().

Upstream references:

Current workaround (to be removed/simplified)

  • src/api/providers/gemini/obsidian-fetch.ts — a requestUrl-backed fetch adapter (obsidianFetch) and an idempotent, defensive installer (installObsidianFetch).
  • src/api/providers/gemini/client.tsgenerateViaInteractions() calls installObsidianFetch(this.ai) before each Interactions call.
  • Tests: test/api/providers/gemini/obsidian-fetch.test.ts, plus the routing assertion in client.test.ts.

How to check whether Google has fixed it

A. Has the SDK dropped the Api-Revision header? Bump @google/genai, then check whether the Next-Gen client still sets it:

grep -rn "Api-Revision\|api-revision" node_modules/@google/genai/dist/

If gone, the preflight trigger is removed.

B. Has the server added the header to its CORS allowlist? (from #1723) — the preflight should return 200 with Api-Revision echoed:

# Should now return HTTP/2 200 with access-control-allow-origin when api-revision is requested
curl -si -X OPTIONS \
  -H 'Origin: http://localhost' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type,x-goog-api-key,api-revision' \
  https://generativelanguage.googleapis.com/v1beta/interactions | grep -i '^HTTP/\|access-control-allow'

C. Has the SDK added a public custom-fetch hook? (tracking #999 / #1215) — if GoogleGenAIOptions / httpOptions gains a supported fetch field, we can stop reaching into getNextGenClient() and pass our adapter through the public API instead.

D. End-to-end check in Obsidian: turn off the workaround (or temporarily make installObsidianFetch a no-op), enable Use Interactions API, and send an agent message. If it succeeds without Failed to fetch, the upstream issue is resolved.

What to do once fixed

  • If A (header removed) or B (server allowlist updated): the renderer fetch works again — the requestUrl routing becomes optional. Decide whether to remove it or keep it as belt-and-suspenders (it's harmless and consistent with the plugin's other network calls; streaming in Interactions API — Phase 2: streaming rewrite (step-based events) #1015 may still benefit from requestUrl for SSE handling — evaluate then).
  • If C (public fetch hook): replace the getNextGenClient() instance-patch with the supported option to drop the dependency on SDK internals, even if we keep using requestUrl.

Acceptance

  • Verified upstream status via the checks above and recorded the result here.
  • Workaround removed or deliberately retained with a documented rationale; obsidian-fetch.ts comment + this issue updated accordingly.

Related: #1013 (epic), #1014 (Phase 1), #1015 (Phase 2 streaming — re-evaluate requestUrl need for SSE).

Metadata

Metadata

Assignees

No one assigned

    Labels

    architecturePlugin architecture and internalsauto:in-progressAuto-dev is implementing this issue / has an open PR for itenhancementNew feature or requestmodels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions