paperclipai/paperclip · error

paperclip_runner_chat_attachment_read_arguments_invalid

paperclip_runner_chat_attachment_read_arguments_invalid

Error message

paperclip_runner_chat_attachment_read_arguments_invalid

What it means

read() validates its input strictly before doing any work: both sourceCommentId and attachmentId must be UUID-shaped strings, and the input object may contain no extra keys (additionalProperties false). Any violation throws this error synchronously. It is a pre-flight guard for the read_chat_attachment tool contract.

Solutions

  1. Obtain both ids verbatim from the list_chat_attachments tool output; never construct them by hand.
  2. Validate UUID format before calling read().
  3. Strip any extra keys from the input object; only sourceCommentId and attachmentId are accepted.
  4. If a new parameter is needed, update READ_CHAT_ATTACHMENT_TOOL_DEFINITION's inputSchema and this check together.

Example fix

// before
await scope.read({ sourceCommentId: "42", attachmentId: id, path: "./file" });
// after
await scope.read({ sourceCommentId: "9f0c...-uuid", attachmentId: "2ab1...-uuid" });
Defensive patterns

Strategy: validation

Validate before calling

const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function canRead(input) {
  return input != null
    && UUID.test(String(input.sourceCommentId))
    && UUID.test(String(input.attachmentId))
    && Object.keys(input).every(k => k === "sourceCommentId" || k === "attachmentId");
}

Type guard

function isValidReadInput(input: unknown): input is { sourceCommentId: string; attachmentId: string } {
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  if (typeof input !== "object" || input === null) return false;
  const o = input as Record<string, unknown>;
  return typeof o.sourceCommentId === "string" && UUID.test(o.sourceCommentId)
    && typeof o.attachmentId === "string" && UUID.test(o.attachmentId)
    && Object.keys(o).length === 2;
}

Try / catch

try {
  const file = await scope.read(input);
} catch (e) {
  if (e.message === "paperclip_runner_chat_attachment_read_arguments_invalid") return { status: "bad_tool_arguments" };
  throw e;
}

Prevention

When it happens

Trigger: Calling read() with a non-UUID or empty sourceCommentId/attachmentId; passing extra properties in the input object (e.g. a guessed 'path' or 'commentId' key); passing null/undefined/numeric ids; a model emitting malformed tool arguments.

Common situations: An LLM hallucinating tool arguments that don't match the JSON schema; hand-rolled tool calls using display ids instead of the UUIDs returned by list_chat_attachments; adding a new input field without updating the schema.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/436347ea0b63f349. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/native-runtime/chat-attachment-read.ts:77

      storageTimeoutMs?: number;
    },
  ) {}

  #assertOpen() {
    if (this.#closed)
      throw new Error("paperclip_runner_chat_attachment_read_scope_closed");
  }

  read(input: { sourceCommentId: string; attachmentId: string }) {
    this.#assertOpen();
    if (
      !UUID.test(input.sourceCommentId) ||
      !UUID.test(input.attachmentId) ||
      Object.keys(input).some(
        (key) => key !== "sourceCommentId" && key !== "attachmentId",
      )
    ) {
      throw new Error(
        "paperclip_runner_chat_attachment_read_arguments_invalid",
      );
    }
    if (this.options.executionTargetKind !== "local") {
      throw new Error(
        "paperclip_runner_chat_attachment_remote_staging_unsupported",
      );
    }
    if (++this.#readCount > 20)
      throw new Error("paperclip_runner_chat_attachment_read_limit");
    const pending = this.#read(input);
    this.#pending.add(pending);
    void pending
      .finally(() => this.#pending.delete(pending))
      .catch(() => undefined);
    return pending;
  }

View on GitHub (pinned to 3f1d897a7c)