Skip to content

Commit 1fa99cc

Browse files
author
Nisanth Chunduru
committed
Delete itemId property before request is signed to prevent signature mismatch errors (for all messages except the first one)
The Canonical String for this request should have been 'POST /openai/v1/responses host:bedrock-mantle.us-east-2.api.aws x-amz-date:20260608T042332Z x-amz-security-token:[REDACTED] x-session-affinity:[REDACTED] host;x-amz-date;x-amz-security-token;x-session-affinity 0074d11c618029c7ba55454a32a868e575e7c56cfd05b8af459574d0873ee672' The String-to-Sign should have been 'AWS4-HMAC-SHA256 20260608T042332Z 20260608/us-east-2/bedrock-mantle/aws4_request 5c37a23ebffa60def8aef51b58c23ce169e92b4fb0a80fb8f4fa648b9a8ceac4'
1 parent b5cb9aa commit 1fa99cc

3 files changed

Lines changed: 112 additions & 3 deletions

File tree

‎packages/opencode/src/provider/provider.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,9 +1722,7 @@ export const layer = Layer.effect(
17221722

17231723
// Strip openai itemId metadata following what codex does
17241724
if (
1725-
(model.api.npm === "@ai-sdk/openai" ||
1726-
model.api.npm === "@ai-sdk/azure" ||
1727-
model.api.npm === "@ai-sdk/amazon-bedrock/mantle") &&
1725+
(model.api.npm === "@ai-sdk/openai" || model.api.npm === "@ai-sdk/azure") &&
17281726
opts.body &&
17291727
opts.method === "POST"
17301728
) {

‎packages/opencode/src/provider/transform.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,27 @@ function unsupportedParts(msgs: ModelMessage[], model: Provider.Model): ModelMes
409409
})
410410
}
411411

412+
function stripOpenAIResponseItemIds(msgs: ModelMessage[]): ModelMessage[] {
413+
const strip = (opts: Record<string, any> | undefined) => {
414+
if (!opts?.openai || !("itemId" in opts.openai)) return opts
415+
const openai = { ...opts.openai }
416+
delete openai.itemId
417+
return { ...opts, openai }
418+
}
419+
420+
return msgs.map((msg) => {
421+
if (!Array.isArray(msg.content)) return { ...msg, providerOptions: strip(msg.providerOptions) }
422+
return {
423+
...msg,
424+
providerOptions: strip(msg.providerOptions),
425+
content: msg.content.map((part) => {
426+
if (part.type === "tool-approval-request" || part.type === "tool-approval-response") return { ...part }
427+
return { ...part, providerOptions: strip(part.providerOptions) }
428+
}),
429+
} as typeof msg
430+
})
431+
}
432+
412433
export function message(msgs: ModelMessage[], model: Provider.Model, options: Record<string, unknown>) {
413434
msgs = unsupportedParts(msgs, model)
414435
msgs = normalizeMessages(msgs, model, options)
@@ -453,6 +474,10 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re
453474
})
454475
}
455476

477+
if (model.api.npm === "@ai-sdk/amazon-bedrock/mantle" && options.store !== true) {
478+
msgs = stripOpenAIResponseItemIds(msgs)
479+
}
480+
456481
return msgs
457482
}
458483

‎packages/opencode/test/provider/transform.test.ts‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,92 @@ describe("ProviderTransform.message - strip openai metadata when store=false", (
18951895
expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_123")
18961896
})
18971897

1898+
test("strips itemId for Bedrock Mantle with store=false", () => {
1899+
const mantleModel = {
1900+
...openaiModel,
1901+
providerID: "amazon-bedrock",
1902+
api: {
1903+
id: "openai.gpt-5.5",
1904+
url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
1905+
npm: "@ai-sdk/amazon-bedrock/mantle",
1906+
},
1907+
}
1908+
const msgs = [
1909+
{
1910+
role: "assistant",
1911+
providerOptions: {
1912+
openai: {
1913+
itemId: "msg_root",
1914+
otherOption: "root-value",
1915+
},
1916+
},
1917+
content: [
1918+
{
1919+
type: "reasoning",
1920+
text: "thinking...",
1921+
providerOptions: {
1922+
openai: {
1923+
itemId: "rs_123",
1924+
reasoningEncryptedContent: "encrypted",
1925+
},
1926+
},
1927+
},
1928+
{
1929+
type: "text",
1930+
text: "Hello",
1931+
providerOptions: {
1932+
openai: {
1933+
itemId: "msg_456",
1934+
otherOption: "value",
1935+
},
1936+
},
1937+
},
1938+
],
1939+
},
1940+
] as any[]
1941+
1942+
const result = ProviderTransform.message(msgs, mantleModel, { store: false }) as any[]
1943+
1944+
expect(result[0].providerOptions?.openai?.itemId).toBeUndefined()
1945+
expect(result[0].providerOptions?.openai?.otherOption).toBe("root-value")
1946+
expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined()
1947+
expect(result[0].content[0].providerOptions?.openai?.reasoningEncryptedContent).toBe("encrypted")
1948+
expect(result[0].content[1].providerOptions?.openai?.itemId).toBeUndefined()
1949+
expect(result[0].content[1].providerOptions?.openai?.otherOption).toBe("value")
1950+
})
1951+
1952+
test("preserves itemId for Bedrock Mantle with store=true", () => {
1953+
const mantleModel = {
1954+
...openaiModel,
1955+
providerID: "amazon-bedrock",
1956+
api: {
1957+
id: "openai.gpt-5.5",
1958+
url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
1959+
npm: "@ai-sdk/amazon-bedrock/mantle",
1960+
},
1961+
}
1962+
const msgs = [
1963+
{
1964+
role: "assistant",
1965+
content: [
1966+
{
1967+
type: "text",
1968+
text: "Hello",
1969+
providerOptions: {
1970+
openai: {
1971+
itemId: "msg_123",
1972+
},
1973+
},
1974+
},
1975+
],
1976+
},
1977+
] as any[]
1978+
1979+
const result = ProviderTransform.message(msgs, mantleModel, { store: true }) as any[]
1980+
1981+
expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_123")
1982+
})
1983+
18981984
test("preserves metadata for non-openai packages when store is false", () => {
18991985
const anthropicModel = {
19001986
...openaiModel,

0 commit comments

Comments
 (0)