Skip to content

callModel with plain string input and existing state produces invalid responsesRequest.input array, causing SDKValidationError #58

Description

@flexdinesh

Bug description

When calling callModel with a plain string input while reusing an existing StateAccessor, the SDK builds a responsesRequest.input array that contains the raw string as the last element instead of normalizing it into a user message object. The OpenRouter SDK then rejects the request with a SDKValidationError.

This contradicts the documented "Multi-Run Conversations" example, which explicitly passes a plain string for subsequent turns. Input formats docs section also suggests that all input formats are accepted.

Reproduction

  1. Start a conversation with callModel using a state accessor. Use a model that returns reasoning/output items (e.g. minimax/minimax-m2.7).
import { OpenRouter } from "@openrouter/agent";

const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });

const state = createStateAccessor("conv-123");

const r1 = openrouter.callModel({
  model: "minimax/minimax-m2.7",
  input: "hello world",
  state,
});

console.log(await r1.getText());
  1. Resume the same conversation with a new plain string message.
const r2 = openrouter.callModel({
  model: "minimax/minimax-m2.7",
  input: "what was my first message",
  state,
});

console.log(await r2.getText());

Expected behavior

The second call should succeed, with the SDK normalizing the plain string into a user message and merging it with the persisted history, as shown in the docs:

https://openrouter.ai/docs/agent-sdk/call-model/tool-approval-state#multi-run-conversations

Actual behavior

The second call throws:

SDKValidationError: Input validation failed
  path: ["responsesRequest", "input"]
  invalid_union: expected string, received array
  at path [3]: expected object, received string

The SDK merges the new plain string directly into the history array, producing something like:

[
  { role: "user", content: "hello world" },
  { type: "reasoning", content: [...] },
  { role: "assistant", type: "message", content: [...] },
  "what was my first message",  // <-- raw string, invalid
]

Environment

  • @openrouter/agent: 0.7.2
  • @openrouter/sdk: 0.13.21
  • Node.js: 26.3.1
  • Model: minimax/minimax-m2.7

Workaround

Pass the new turn as an array of user message objects instead of a plain string:

const r2 = openrouter.callModel({
  model: "minimax/minimax-m2.7",
  input: [{ role: "user", content: "what was my first message" }],
  state,
});

Additional context

Looking at the installed package source, the issue appears to be in @openrouter/agent/esm/lib/model-result.js. When state is present and newInput is not an array, the SDK does:

freshItems = Array.isArray(newInput) ? newInput : [newInput];
baseRequest.input = appendToMessages(historicalMessages, hookedFresh);

This appends the raw string to the history array without converting it into a valid EasyInputMessage object. The docs imply the SDK should normalize a plain string into a user message before merging.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions