paperclipai/paperclip · error

paperclip_runner_chat_attachment_read_integrity_mismatch

paperclip_runner_chat_attachment_read_integrity_mismatch

Error message

paperclip_runner_chat_attachment_read_integrity_mismatch

What it means

After fully reading the stream, #bytes verifies the total length equals source.byteSize and that the SHA-256 of the body matches source.sha256. Any mismatch means the bytes delivered by storage do not correspond to the authorized attachment, so the content is discarded and this error is thrown. This is the final content-integrity gate before staging.

Solutions

  1. Re-upload the attachment so bytes and sha256/byteSize metadata agree, then retry the read.
  2. Check the storage backend for truncation/corruption (multipart completeness, checksums).
  3. Fix any writer that mutates object bytes without updating the recorded sha256.
  4. Retry once to rule out a transient truncated transfer.

Example fix

// before
await replaceObject(key, newBytes); // metadata sha256 now stale
// after
await replaceObject(key, newBytes, { sha256: sha256(newBytes), byteSize: newBytes.byteLength });
Defensive patterns

Strategy: try-catch

Type guard

function isIntegrityError(e: unknown): boolean {
  return e instanceof Error && e.message === "paperclip_runner_chat_attachment_read_integrity_mismatch";
}

Try / catch

try {
  return await scope.read(input);
} catch (e) {
  if (isIntegrityError(e)) {
    return { status: "integrity_failure" }; // re-upload the attachment; never stage unverified bytes
  }
  throw e;
}

Prevention

When it happens

Trigger: Storage returns truncated or corrupted bytes (short read, interrupted transfer); object content was mutated without updating metadata; hash-case mismatch after a buggy writer stored an uppercase hash; partial upload committed with stale sha256 metadata.

Common situations: Flaky storage backends dropping stream tails; bit-rot or manual object replacement in the bucket; re-encryption pipelines changing bytes but not metadata.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

        for await (const chunk of value.stream) {
          const bytes = Buffer.from(chunk);
          length += bytes.length;
          if (length > source.byteSize || length > MAX_ATTACHMENT_BYTES)
            throw new Error(
              "paperclip_runner_chat_attachment_read_size_mismatch",
            );
          chunks.push(bytes);
        }
      } finally {
        value.stream.destroy();
      }
      const body = Buffer.concat(chunks);
      if (
        length !== source.byteSize ||
        createHash("sha256").update(body).digest("hex") !==
          source.sha256.toLowerCase()
      )
        throw new Error(
          "paperclip_runner_chat_attachment_read_integrity_mismatch",
        );
      return body;
    })();
    return Promise.race([read, aborted]);
  }

  close(): Promise<void> {
    if (this.#closing) return this.#closing;
    this.#closed = true;
    this.#abort.abort();
    this.#closing = (async () => {
      await Promise.allSettled([...this.#pending]);
      const results = await Promise.allSettled(
        this.#cleanups.map((cleanup) => cleanup()),
      );
      if (results.some((result) => result.status === "rejected"))
        throw new Error("paperclip_runner_chat_attachment_read_cleanup_failed");

View on GitHub (pinned to 3f1d897a7c)