paperclipai/paperclip · error

Semantic result disposition does not match the terminal tool

Error message

Semantic result disposition does not match the terminal tool

What it means

The block tool (`prp_block`) must carry `reportedWorkDisposition === "blocked"` and the completion tool (`prp_completion`) must not. If the agent's structured result disposition contradicts which terminal tool it called, the driver rejects it. This keeps the machine-readable result consistent with the terminal action the agent took.

Source

Thrown at packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts:898

        item: {
          type: "tool_call",
          id: call.callId,
          name: tool,
          arguments: call.arguments,
        },
      },
      { turnId, itemId: call.callId },
    );
    if (tool === PRP_COMPLETION_TOOL_NAME || tool === PRP_BLOCK_TOOL_NAME) {
      const validation = validatePrpStructuredRunResult(call.arguments);
      if (!validation.ok) throw new Error("Invalid semantic result");
      if (
        (tool === PRP_BLOCK_TOOL_NAME &&
          validation.result.reportedWorkDisposition !== "blocked") ||
        (tool === PRP_COMPLETION_TOOL_NAME &&
          validation.result.reportedWorkDisposition === "blocked")
      )
        throw new Error(
          "Semantic result disposition does not match the terminal tool",
        );
      if (
        validation.result.completionClaim.contractRevision !==
        this.#taskEnvelope.completionContract.revision
      ) {
        throw new Error(
          "Semantic result completion contract revision does not match",
        );
      }
      const fingerprint = canonicalJson(validation.result);
      if (this.#resultFingerprint && this.#resultFingerprint !== fingerprint)
        throw new Error("A different semantic result was already committed");
      if (!this.#resultFingerprint) {
        this.#result = structuredClone(validation.result);
        this.#resultFingerprint = fingerprint;
        this.#resultCallId = call.callId;
        this.#resultTurnId = turnId;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Retry the turn with explicit instruction: blocked work must use `prp_block` with `reportedWorkDisposition: "blocked"`; finished work must use `prp_completion` with a non-blocked disposition.
  2. Check the agent's system instructions/task envelope to confirm the two terminal tools and their dispositions are documented unambiguously.
  3. Inspect the failing call's arguments in the `item.started` event to confirm which mismatch occurred, then correct the agent's decision procedure.
  4. If the agent genuinely finished despite claiming blocked (or vice versa), clarify the work state and have it re-emit the correct terminal tool call.

Example fix

// before
prp_completion({ reportedWorkDisposition: "blocked", ... }) // mismatch

// after
prp_block({ reportedWorkDisposition: "blocked", summary: "waiting on API credentials", ... })
Defensive patterns

Strategy: validation

Validate before calling

const ok = (tool === 'prp_block') === (result.reportedWorkDisposition === 'blocked');
if (!ok) throw new Error('disposition/tool mismatch');

Type guard

function dispositionMatchesTool(tool, result) {
  return tool === 'prp_block' ? result.reportedWorkDisposition === 'blocked'
       : result.reportedWorkDisposition !== 'blocked';
}

Try / catch

try {
  await session.dispatchTool({ tool, callId, arguments });
} catch (e) {
  if (e.message.includes('disposition does not match the terminal tool')) {
    // instruct the agent to re-emit with the matching tool
  } else throw e;
}

Prevention

When it happens

Trigger: Agent calls `prp_completion` with `reportedWorkDisposition: "blocked"`, or calls `prp_block` with a disposition other than `"blocked"` (e.g. `"completed"`), after passing basic schema validation.

Common situations: The agent blocked on a missing dependency but picked the completion tool by mistake; prompt instructions describing the two tools are ambiguous or contradictory; a model copies a template from the other tool.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/8b9c97b6c431a56e. Report an issue: GitHub.