Skip to content

[Tooling] [Bug] Veo video URLs returned by predictLongRunning are missing API key (leads to 403 Forbidden in Dev UI) #4025

@CatoPaus

Description

@CatoPaus

Bug Report: [google-genai] Veo video URLs returned by predictLongRunning are missing API key (leads to 403 Forbidden in Dev UI)

Description

When using the googleai/veo-* models (via the @genkit-ai/google-genai plugin), the video generation successfully completes, but the media.url returned in the output is a private Google Cloud URI that requires authentication.

The current implementation returns the raw URI from the API response (e.g., https://generativelanguage.googleapis.com/...:download?alt=media). When the Genkit Developer UI attempts to load this URL in a <video> tag or download link, it receives a 403 Forbidden error because the request is unauthenticated.

Steps to Reproduce

  1. Configure Genkit with @genkit-ai/google-genai plugin using a valid apiKey.
  2. Start the Genkit Developer UI (npx genkit start).
  3. Select a Veo model (e.g., googleai/veo-3.0-generate-001).
  4. Generate a video with a simple prompt.
  5. Wait for generation to complete.
  6. Observation: The video does not play in the UI, and the download link returns a 403 error loop or XML error page when clicked.

Expected Behavior

The returned media.url should be usable by the client. The plugin should likely append ?key=<API_KEY> to the URL so that the browser can fetch the private resource, similar to how other private assets might be handled or how the API expects valid requests.

Environment

  • Genkit Version: 1.27.0
  • Plugin: @genkit-ai/google-genai
  • Node Version: 23.3.0

Proposed Fix

In src/googleai/veo.ts (specifically fromVeoOperation), the code should append the API key to the URI if it is permitted to do so for client-side rendering.

Example Patch:

function fromVeoOperation(apiOp: any, apiKey?: string) {
  // ... existing code ...
  if (apiOp.response?.generateVideoResponse?.generatedSamples) {
     const samples = apiOp.response.generateVideoResponse.generatedSamples.map((s) => {
        let videoUrl = s.video.uri;
        if (apiKey) {
           videoUrl += (videoUrl.includes('?') ? '&' : '?') + 'key=' + apiKey;
        }
        return { media: { url: videoUrl } };
     });
     // ...
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtooling

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions