paperclipai/paperclip · error · Error

paperclip_runner_tool_input_invalid

paperclip_runner_tool_input_invalid

Error message

paperclip_runner_tool_input_invalid

What it means

Generic input-validation sentinel for runner tools. In #reuseChatAttachment it is thrown when the idempotencyKey exceeds 200 characters (and elsewhere when requiredString/requiredUuid checks fail). It signals the tool arguments are structurally invalid before any authorization or database work happens.

Solutions

  1. Shorten the idempotencyKey to 200 characters or fewer (hash long composite keys, e.g. sha256 hex).
  2. Ensure every required field is present: idempotencyKey (string), sourceCommentId (UUID), attachmentId (UUID), title (string).
  3. Validate UUID format (RFC 4122 version 1-5, variant 89ab) before calling; strip any prefixes from ids.
  4. Keep title at 500 characters or fewer (see error 334).

Example fix

// before
const key = `reuse:${issueId}:${attachmentId}:${Date.now()}-${longSalt}`; // >200 chars

// after
import { createHash } from 'node:crypto';
const key = createHash('sha256').update(`reuse:${issueId}:${attachmentId}`).digest('hex');
Defensive patterns

Strategy: validation

Validate before calling

const key = String(input.idempotencyKey ?? '');
if (!key || key.length > 200) throw new Error('idempotencyKey required, max 200 chars');
if (!input.sourceCommentId || !input.attachmentId) throw new Error('sourceCommentId and attachmentId required');

Type guard

const isUuid = (v: unknown): v is string =>
  typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(v);

Prevention

When it happens

Trigger: Calling the reuse_chat_attachment tool with an idempotencyKey longer than 200 chars, or omitting/malforming idempotencyKey, sourceCommentId, attachmentId, or title so requiredString/requiredUuid throw the same sentinel.

Common situations: Agents embedding long UUIDs+timestamps+salts into idempotency keys, passing null/undefined arguments, or sending non-UUID strings (e.g. prefixed ids like 'comment_123') for sourceCommentId/attachmentId.

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/a8b7c93efad2a464. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/native-runtime/paperclip-runner-tool-authority.ts:998

        }
        return prepared.result;
      },
      {
        onDefinitePreCommitFailure: async () => {
          const rollback = rollbackDefinitePreCommitFailure;
          rollbackDefinitePreCommitFailure = null;
          await rollback?.();
        },
      },
    );
    if (publication) publishActivity(publication);
    return result;
  }

  async #reuseChatAttachment(input: Record<string, unknown>): Promise<unknown> {
    const idempotencyKey = requiredString(input.idempotencyKey);
    if (idempotencyKey.length > 200) {
      throw new Error("paperclip_runner_tool_input_invalid");
    }
    const sourceCommentId = requiredUuid(input.sourceCommentId);
    const attachmentId = requiredUuid(input.attachmentId);
    const title = requiredString(input.title);
    if (title.length > 500) throw new Error("paperclip_runner_tool_input_invalid");
    let source: ChatAttachmentReuseSource | null = null;
    let publication:
      Awaited<ReturnType<typeof persistActivity>>["publication"] | null = null;
    let rollbackDefinitePreCommitFailure: (() => Promise<void>) | null = null;
    const authorize = async (tx: Db, contextSnapshot: unknown) => {
      source = await authorizeChatAttachmentReuse({
        db: tx,
        binding: this.binding,
        contextSnapshot,
        sourceCommentId,
        attachmentId,
      });
    };

View on GitHub (pinned to 3f1d897a7c)