Skip to content

Resolve pre-flight task failures synchronously (payment, standby) #684

Description

@jirispilka

Mechanism revised 2026-07-10 (implemented in #1080). The original proposal — return paymentRequiredResult directly and skip task creation entirely — cannot ship: SDK 1.29.0's Server.setRequestHandler validates every task-augmented tools/call response against CreateTaskResultSchema (requires a task field), so a bare CallToolResult surfaces as McpError(InvalidParams) instead of the x402 payload. The shipped fix resolves the task synchronously at creation instead: same goals (no zombie working phase, faster x402 delivery, dead code removed), SDK-compliant wire shape.

Problem

When paymentRequiredResult is set (missing payment signature), we still create a task and pass the error into executeToolAndUpdateTask(), which immediately stores it as a completed-with-error result via an async setImmediate hop. The outcome is known before execution: the client sees a fake working phase, and the task path carries dead re-check code. Same applies to standbyRejection and any future synchronously-resolvable pre-flight check.

Fix (as shipped in #1080)

In the handler's task branch, right after createTask: if a pre-flight value is set (standby priority first, matching the sync path), store it as the terminal completed result, emit a single completed status notification (no working transition), and return { task: { ...task, status: 'completed' } }. Telemetry rides the handler's finally, firing once with the same properties as the non-task path. The dead standby/402 re-checks and their two carrier params are deleted from executeToolAndUpdateTask.

Sequencing

Sub-issue of umbrella #658. Landed after #1061 (behavior-pinning tests, PR #1073) and before #1063 (dispatchToolCall extraction) — removing the pre-flight checks from the task path shrinks that extraction.

Wire-visible change (flagged for the internal contract suite in #1080): a task-augmented call that fails pre-flight now returns a CreateTaskResult that is already completed; previously it transitioned through working and completed asynchronously. Notification timing changes accordingly (single completed, no working).

Context

Discovered during #680 (task status workaround).

Metadata

Metadata

Assignees

No one assigned

    Labels

    t-aiIssues owned by the AI team.

    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