Skip to content

X-Server-Timeout header not sent when httpOptions.timeout is set without httpOptions.headers #1489

@daniel-smyth

Description

@daniel-smyth

Description

When setting httpOptions.timeout on a generateContent request (or any request), the SDK correctly applies a client-side timeout via AbortController, but fails to send the X-Server-Timeout header to the server. This means the Vertex AI backend uses its default server-side timeout (5 minutes / 300s), regardless of the client-side timeout value.

Root cause

In getHeadersInternal, the X-Server-Timeout header is only appended when httpOptions.headers is truthy:

async getHeadersInternal(httpOptions, url) {
    const headers = new Headers();
    if (httpOptions && httpOptions.headers) {           // <-- gates on .headers
        for (const [key, value] of Object.entries(httpOptions.headers)) {
            headers.append(key, value);
        }
        // X-Server-Timeout is nested inside the .headers check
        if (httpOptions.timeout && httpOptions.timeout > 0) {
            headers.append(SERVER_TIMEOUT_HEADER, String(Math.ceil(httpOptions.timeout / 1000)));
        }
    }
    await this.clientOptions.auth.addAuthHeaders(headers, url);
    return headers;
}

If a user passes httpOptions: { timeout: 600000 } without a headers property, httpOptions.headers is undefined, so the entire block is skipped — including the X-Server-Timeout logic.

Expected behavior

The X-Server-Timeout header should be sent whenever httpOptions.timeout is set, regardless of whether httpOptions.headers is provided.

Suggested fix

Move the X-Server-Timeout logic outside the httpOptions.headers check:

async getHeadersInternal(httpOptions, url) {
    const headers = new Headers();
    if (httpOptions && httpOptions.headers) {
        for (const [key, value] of Object.entries(httpOptions.headers)) {
            headers.append(key, value);
        }
    }
    // Should not be nested inside the .headers check
    if (httpOptions && httpOptions.timeout && httpOptions.timeout > 0) {
        headers.append(SERVER_TIMEOUT_HEADER, String(Math.ceil(httpOptions.timeout / 1000)));
    }
    await this.clientOptions.auth.addAuthHeaders(headers, url);
    return headers;
}

Workaround

Pass an empty headers object alongside timeout:

httpOptions: {
  timeout: 600000,
  headers: {},    // workaround: makes the headers check truthy
}

Reproduction

const genai = new GoogleGenAI({ vertexai: true, project: '...', location: '...' });

// X-Server-Timeout header is NOT sent — server uses its 5-minute default
const response = await genai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: '...',
  config: {
    httpOptions: { timeout: 600000 },
  },
});

Environment

  • @google/genai version: 1.42.0
  • Runtime: Node.js
  • Using: Vertex AI

Metadata

Metadata

Assignees

Labels

api: vertex-aiIssues related to the Vertex AI API.priority: p2Moderately-important priority. Fix may not be included in next release.status:awaiting user responseissues requiring a response from the userstatus:staleIssues tagged for cleanup by the stale issue workflow.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions