Skip to content

Clone anthropic MessageNewParams slices to avoid mutating caller state#529

Open
PratikDhanave wants to merge 3 commits into
microsoft:mainfrom
PratikDhanave:fix-anthropic-params-aliasing
Open

Clone anthropic MessageNewParams slices to avoid mutating caller state#529
PratikDhanave wants to merge 3 commits into
microsoft:mainfrom
PratikDhanave:fix-anthropic-params-aliasing

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

Problem

buildMessageParams adopts the caller-provided MessageNewParams option by struct copy:

if p, ok := agent.GetOption(opts, MessageNewParams); ok {
    params = p
}

It then appends to params.System (system instructions) and params.Messages. A struct copy shares the slice backing arrays with the caller's value, so:

  • If the caller's slice has spare capacity, the append writes into the caller's spare slots — silently corrupting their data.
  • A caller that reuses the same MessageNewParams across runs accumulates duplicated system blocks.

The gemini provider already clones for exactly this reason (provider/geminiprovider/agent.go), so this also aligns the two providers.

Fix

Clone the two mutated slice fields right after adopting the option:

params = p
params.System = slices.Clone(params.System)
params.Messages = slices.Clone(params.Messages)

Test

TestBuildMessageParams_DoesNotMutateCallerSystemSlice (black-box, in agent_test.go) passes a System slice with spare capacity and asserts the caller's backing array is untouched after a run. It fails on main (spare slot = "added") and passes with the fix.

buildMessageParams took the caller-provided MessageNewParams option by
struct copy (params = p) and then appended system instructions and
messages to params.System / params.Messages. Because the struct copy
shares the caller's slice backing arrays, appending within spare capacity
silently overwrote the caller's data, and a reused params value would
accumulate duplicated system blocks across runs.

Clone the two mutated slice fields after adopting the option, mirroring
what the gemini provider already does for the same reason. Adds a
black-box test asserting the caller's System backing array is untouched.
@PratikDhanave
PratikDhanave requested a review from a team as a code owner July 17, 2026 18:56
Copilot AI review requested due to automatic review settings July 17, 2026 18:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents the Anthropic provider from accidentally mutating caller-supplied anthropic.MessageNewParams slice backing arrays when buildMessageParams appends system instructions and messages, aligning behavior with the existing Gemini provider approach. It also adds a regression test to ensure caller slice state is preserved.

Changes:

  • Clone MessageNewParams.System and MessageNewParams.Messages immediately after adopting the MessageNewParams option to avoid aliasing the caller’s slice backing arrays.
  • Add a regression test that validates building a request does not mutate a caller-provided System slice with spare capacity.
  • Minor formatting/gofmt adjustments in the Anthropic provider test helper/config test.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
provider/anthropicprovider/agent.go Clones System and Messages slices after adopting MessageNewParams to avoid mutating caller state when appending.
provider/anthropicprovider/agent_test.go Adds a regression test ensuring caller-provided System slice backing array is not mutated; includes minor formatting changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +472 to +476
// Caller-supplied System slice with spare capacity.
system := make([]anthropic.TextBlockParam, 1, 4)
system[0] = anthropic.TextBlockParam{Text: "s0"}
opt := anthropicprovider.MessageNewParams(anthropic.MessageNewParams{System: system})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added TestBuildMessageParams_DoesNotMutateCallerMessagesSlice, a red-green regression test covering a caller-supplied Messages slice with spare capacity.

Address review feedback: the fix clones both System and Messages, but the
test only covered System. Add a regression test asserting a caller-supplied
Messages slice with spare capacity is not mutated.
# Conflicts:
#	provider/anthropicprovider/agent_test.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants