Skip to content

[Compatibility] cursor emits string tool arguments as bare integers (cell_id: 4) and routed calls are rejected before execution - mirror of #1611 #1938

Description

@ayoahha

Client or integration

Other

Provider or upstream service

Codex-CLI

OpenCodex version

2.24.1

Endpoint or capability

/v1/chat/completions and /v1/responses - tool calls

Current behaviour

A JSON integer emitted by the provider in a string-declared field passes through the bridge untouched. Codex then rejects the call before the tool runs. Affected in practice: Codex's code-mode wait tool (cell_id declared {"type": "string"}). Through cursor/gpt-5.6-sol, every wait call arrives as {"cell_id":4,...} and fails. Measured across 3 full Codex sessions: 19/19 wait calls rejected. The model never self-corrects (it retries varying yield_time_ms/max_tokens), so this is a hard failure loop, not a degradation - same failure shape as #1611. Prompt-level mitigation does not work: with an explicit rule in context ("cell_id is a STRING, always quote it") the provider still emitted the integer on every call.

Expected behaviour

The proxy repairs the representation artifact against the declared schema, as it already does for the mirror case: #1611 fixed integer-declared fields arriving as integral floats (120000.0 -> 120000). A JSON integer in a string-declared field has exactly one faithful string reading (4 -> "4"), so it can be repaired under the same boundary logic; any non-integer value in a string field stays untouched and still fails, so no plausible lie is manufactured.

Minimal redacted request or reproduction

# Proxy running on the default port. Tool schema declares cell_id as a string.
curl -s http://127.0.0.1:10100/v1/chat/completions -H 'Content-Type: application/json' -d '{
  "model": "cursor/gpt-5.6-sol",
  "messages": [
    {"role": "system", "content": "You manage exec cells. When a script yields, resume it with the wait tool."},
    {"role": "user", "content": "Script running with cell ID 4. Resume it and get its output."}
  ],
  "tools": [{
    "type": "function",
    "function": {
      "name": "wait",
      "description": "Waits on a yielded exec cell and returns new output or completion.",
      "parameters": {
        "type": "object",
        "properties": {
          "cell_id": {"type": "string", "description": "Identifier of the running exec cell."},
          "yield_time_ms": {"type": "number"},
          "max_tokens": {"type": "number"}
        },
        "required": ["cell_id"],
        "additionalProperties": false
      }
    }
  }],
  "tool_choice": "required",
  "max_tokens": 200
}'

Actual response or error

Tool-call arguments returned by the proxy (schema declares cell_id as string):

{"cell_id":4,"yield_time_ms":30000,"max_tokens":4000}

When this reaches Codex (code-mode `wait` tool, same schema), the call is rejected before the tool runs:

failed to parse function arguments: invalid type: integer `4`, expected a string at line 1 column 12

Upstream documentation

https://platform.openai.com/docs/guides/function-calling (tool-call arguments must conform to the declared JSON Schema; strict mode guarantees it). No cursor-specific public spec: expected behaviour is the concrete client requirement of Codex CLI 0.147.0, which parses wait.cell_id as a Rust String and rejects integers.

Suggested mapping or implementation notes

src/lib/tool-argument-integers.ts already does schema-aware repair for integral floats in integer-declared fields, wired at both bridge flush points (streaming and non-streaming). Extending coerceValue with the symmetric string case covers this: when the resolved schema declares "string" (directly or via anyOf/oneOf/allOf branches) and the value is a JSON integer, re-emit it as its decimal string. The cheap-reject regex at the top of coerceIntegerToolArguments (/\d\.\d/) would also need to admit integer payloads for string-declared parameters.

Additional context and attachments

Mirror of #1611 (closed): same provider-side serialization-artifact class, opposite direction. Also checked 2.24.2: tool-argument-integers.ts unchanged, case still uncovered.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reports

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions