INVALID_ARGUMENT when using Node.js Fetch

This works in Chrome (open inspector and paste):

async function main() {
  const response = await fetch(
    'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ABC123`,
      },
      body: JSON.stringify({
        model: 'gemini-1.5-flash',
        messages: [{ role: 'user', content: 'Explain to me how AI works' }],
        stream: true,
      }),
    }
  )

  console.log('>>>', response.statusText, await response.text())
}

main()

The exact same code does not work in Node. The exact error is:

Bad Request [{
  "error": {
    "code": 400,
    "message": "Request contains an invalid argument.",
    "status": "INVALID_ARGUMENT"
  }
}

Does anyone know what is going on?

For some reason it always fails for Node version of fetch up-to the latest (23).

Same problem from rust. I’m also using the openai compatibility endpoint. Replicating the request closely with curl works.

Did you find a solution?

Nope. I just couldn’t be bothered. I am sure it is a problem with Google’s service and the likelyhood of them fixing it is zero. I was hoping to bring Gemini to our customers this week though.

1 Like

I guess this is not a priority for Google.

Just took another stab at it. I had to switch from using openssl on my linux box to using a rust-specific tls library. There might be something similar going on in your case.

I’m having the exact same issue. I’m not sure that it’s an openssl problem either since sending a raw request using openssl works:

printf "POST /v1beta/openai/chat/completions HTTP/1.1\r\nHost: generativelanguage.googleapis.com\r\nAuthorization: Bearer $API_KEY\r\nContent-Type: application/json\r\nContent-Length: 96\r\n\r\n{\"model\":\"gemini-1.5-flash\",\"messages\":[{\"role\":\"user\",\"content\":\"Explain to me how AI works\"}]}\r\n" | openssl s_client -quiet -connect generativelanguage.googleapis.com:443

Weirdly, if I import fetch from node-fetch then the request succeeds, but if I import fetch from undici (or use Node’s built-in fetch, which is also undici), then the request is rejected with the INVALID_ARGUMENT response.

I can only assume that the undici request adds a default header which the API doesn’t like.

I was able to reproduce the issue here: link.

Root Cause

The OpenAI compatibility API endpoint does not handle lowercase header names properly, which is common in most HTTP client libraries.

@braincore Most HTTP client crates in Rust rely on the http crate, which always sends headers in lowercase. However, with reqwest, you can enable title case with following code

let client = Client::builder().http1_title_case_headers().build()?;