continue
continue copied to clipboard
IntelliJ: duplicate authorization header sent for OpenAI models, causing authentication failure
Before submitting your bug report
- [x] I've tried using the "Ask AI" feature on the Continue docs site to see if the docs have an answer
- [x] I believe this is a bug. I'll try to join the Continue Discord for questions
- [x] I'm not able to find an open issue that reports the same bug
- [x] I've seen the troubleshooting guide on the Continue Docs
Relevant environment info
- OS: Linux
- Continue version: 1.0.30
- IDE version: IntelliJ IDEA Community 2025.1
- Model: o4-mini (self-hosted)
- config:
models:
- name: my o4-mini
provider: openai
model: o4-mini
apiBase: https://acme.com/some/path
requestOptions:
headers:
Authorization: Bearer eyYo…
my-hdr: bar
Description
When trying to use a self-hosted openai model, the server error response indicates that Continue doesn't send the Authorization header as it should. This does not happen with a self-hosted gemini model, where our server expects the same token and successfully receives it (indicated by absence of error).
Investigating with a proxy, I see this duplication in the raw headers: ['authorization', 'Bearer', 'authorization', 'Bearer [myToken]']. Now the first value, 'Bearer' (token missing), seems to win – at least that's what Node.js' http module makes it into.
Though I can't see the headers received by our server when disabling the proxy, the finding suggests that Continue sends a duplicate Authorization header in my case.
The token has this structure, I would say:
[a-zA-Z0-9]{250}\.[a-zA-Z0-9]{6590}\.[a-zA-Z0-9_-]{342}
I can see the same on VS Code with continue version 1.0.21. My openAI compatible server fails authorization because the empty header takes precedence.
Hi, thank you for the detailed ticket! Taking an initial look at this it seems to be a bug, I'm not very familiar with this portion of the code but I'll create a ticket and surface it to the team!
I'm also having this problem. My case involves using vLLM with basic auth but the problem is essentially the same: no apiKey configured but the provider sends an empty Authorization: Bearer header that interferes with my custom Authorization: Basic <mycredentials> header. I posted a fix proposal as PR https://github.com/continuedev/continue/pull/7803
It looks like the fix in #7803 was included today in vscode version 1.2.7-vscode, but I continue to see this issue.
There is the same issue with Ollama provider when used with the CLI. #7803 fix fixes it for OpenAI , so maybe the same fix would work for Ollama . The same configuration works with Codium extention with no problems, but the CLI client sends two Authorization Headers, e regular Basic and an empty Bearer
It looks like the fix in #7803 was included today in vscode version 1.2.7-vscode, but I continue to see this issue.
Indeed, PR #7803 did not fix the issue for IntelliJ IDEA either, and the double Authorization header kept being sent. Here is a second attempt that I actually had time to test: https://github.com/continuedev/continue/pull/8684
Hello folks I tested this out today on a minimal local proxy server. It seemed to work fine.
Only a single authorization headers remains when used on an express server
Output:
OpenAI proxy request headers: {
accept: "application/json",
"accept-encoding": "gzip, deflate, br",
authorization: "my-key-456",
"content-length": "1901",
"content-type": "application/json",
"user-agent": "OpenAI/JS 5.20.3",
"x-stainless-arch": "arm64",
"x-stainless-lang": "js",
"x-stainless-os": "MacOS",
"x-stainless-package-version": "5.20.3",
"x-stainless-retry-count": "0",
"x-stainless-runtime": "node",
"x-stainless-runtime-version": "v22.20.0",
host: "127.0.0.1:3000",
connection: "close",
}
config.yaml
models:
- name: relay server
provider: openai
model: gpt
apiBase: http://localhost:3000/v1
requestOptions:
headers:
Authorization: "my-key-456"
relay-server.ts
import express, { Request, Response } from "express";
const app = express();
app.use(express.json());
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
const ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages";
if (!ANTHROPIC_API_KEY) {
throw new Error("Missing ANTHROPIC_API_KEY environment variable");
}
type OpenAIChatMessage = {
role: "system" | "user" | "assistant";
content: string;
};
type OpenAIChatCompletionRequest = {
model: string;
messages: OpenAIChatMessage[];
max_tokens?: number;
temperature?: number;
top_p?: number;
stream?: boolean;
};
app.post("/v1/chat/completions", async (req: Request, res: Response) => {
const body = req.body as OpenAIChatCompletionRequest;
console.log("OpenAI proxy request headers:", req.headers);
if (!body || !Array.isArray(body.messages)) {
return res
.status(400)
.json({ error: "Invalid request body: missing messages[]" });
}
if (body.stream) {
return res
.status(400)
.json({ error: "stream=true is not supported by this minimal proxy" });
}
const systemMessages = body.messages.filter((m) => m.role === "system");
const system = systemMessages.map((m) => m.content).join("\n");
const nonSystemMessages = body.messages.filter(
(m) => m.role === "user" || m.role === "assistant"
);
const anthropicPayload = {
model: body.model,
max_tokens: body.max_tokens ?? 1024,
temperature: body.temperature ?? 0.7,
top_p: body.top_p,
system: system || undefined,
messages: nonSystemMessages,
};
try {
const response = await fetch(ANTHROPIC_API_URL, {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": ANTHROPIC_API_KEY!,
"anthropic-version": "2023-06-01",
},
body: JSON.stringify(anthropicPayload),
});
if (!response.ok) {
const errorText = await response.text();
return res
.status(500)
.json({ error: "Anthropic API error", details: errorText });
}
const data: any = await response.json();
const text = (data.content ?? [])
.filter((p: any) => p.type === "text")
.map((p: any) => p.text)
.join("");
const now = Math.floor(Date.now() / 1000);
const openAIResponse = {
id: data.id,
object: "chat.completion",
created: now,
model: body.model,
choices: [
{
index: 0,
message: {
role: "assistant" as const,
content: text,
},
finish_reason: data.stop_reason === "max_tokens" ? "length" : "stop",
},
],
usage: {
prompt_tokens: data.usage?.input_tokens ?? 0,
completion_tokens: data.usage?.output_tokens ?? 0,
total_tokens:
(data.usage?.input_tokens ?? 0) + (data.usage?.output_tokens ?? 0),
},
};
return res.json(openAIResponse);
} catch (err: any) {
console.error(err);
return res
.status(500)
.json({ error: "Unexpected error", details: err.message });
}
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`OpenAI-compatible proxy listening on http://localhost:${port}`);
});
I will need some help with reproduction. Can you help with the config.yaml and an example proxy server that is having the problems?
I will need some help with reproduction.
Hi, and thanks for testing, @uinstinct ! I ran your proxy and it prints only one Authorization header even if multiple are present:
I sent two Authorization headers with curl -X POST http://localhost:3000/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"claude-3-5-sonnet-latest","messages":[{"role":"user","content":"Hello"}],"max_tokens":256}' -H 'Authorization: Bearer foo' -H 'Authorization: Basic bar'
And your proxy code printed out:
OpenAI proxy request headers: {
host: 'localhost:3000',
'user-agent': 'curl/7.88.1',
accept: '*/*',
'content-type': 'application/json',
authorization: 'Bearer foo',
'content-length': '100'
}
And when I listened to port 3000 with netcat instead of your proxy, it printed two Authorization headers for that same curl command:
$ nc -l 3000
POST /v1/chat/completions HTTP/1.1
Host: localhost:3000
User-Agent: curl/7.88.1
Accept: */*
Content-Type: application/json
Authorization: Bearer foo
Authorization: Basic bar
Content-Length: 100
{"model":"claude-3-5-sonnet-latest","messages":[{"role":"user","content":"Hello"}],"max_tokens":256}
Your config.yaml is essentially the same as mine, but here it is in case it helps somehow:
name: Local Agent
version: 1.0.0
schema: v1
models:
- provider: openai
name: openai-test
apiBase: http://localhost:11434
requestOptions:
timeout: 60000
verifySsl: false
headers:
Authorization: Basic bG9sOmJhbA==
model: /models/Intel/Qwen3-30B-A3B-Instruct-2507-int4-AutoRound
roles:
- chat
context:
- provider: code
- provider: docs
- provider: diff
- provider: terminal
- provider: problems
- provider: folder
- provider: codebase
logging:
level: debug
So could you try netcat instead of your proxy, please? The command is nc -l 3000 for BSD netcat and nc -l -p 3000 for the traditional variety of netcat. At least my IntelliJ IDEA 2025.1 (251.23774.435) with Continue plugin version 1.0.52 still sends two Authorization headers.
I see it now!
Verified your fix in #8684 and it seems to work. the openai package was adding the Authorization: Bearer header which was causing 2 authorization headers to be sent.
Thanks for the nc -l 3000! This can be handy for testing
Appreciate the insights all! Merged @visadb's fix
:tada: This issue has been resolved in version 1.31.0 :tada:
The release is available on:
Your semantic-release bot :package::rocket: