paperclipai/paperclip · warning

paperclip_runner_chat_attachment_read_aborted

paperclip_runner_chat_attachment_read_aborted

Error message

paperclip_runner_chat_attachment_read_aborted

What it means

Storage fetches race against an abort signal combining scope closure with a storage timeout (default 5s). If the signal fires — scope closed or storage did not deliver the object in time — the object stream is destroyed and this error is thrown from #bytes via the aborted promise.

Solutions

  1. Increase storageTimeoutMs when reading large attachments or over slow links.
  2. Check storage backend health/latency (S3 region, network path).
  3. Retry the read once; transient storage slowness often resolves.
  4. If scope closure causes it, stop issuing reads once the run is ending.

Example fix

// before
new NativeChatAttachmentReadScope({ ..., storageTimeoutMs: undefined }); // 5s default
// after
new NativeChatAttachmentReadScope({ ..., storageTimeoutMs: 30_000 });
Defensive patterns

Strategy: retry

Type guard

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

Try / catch

try {
  return await scope.read(input);
} catch (e) {
  if (isAbortedError(e)) {
    await new Promise(r => setTimeout(r, 200));
    return scope.read(input); // retry unless the scope was closed
  }
  throw e;
}

Prevention

When it happens

Trigger: Storage backend (S3/local) taking longer than storageTimeoutMs (default 5000ms) to return the object; scope.close() called while a read was streaming; AbortSignal.timeout firing on a slow or hung storage provider.

Common situations: Slow or degraded object storage; large attachments on high-latency links; network partitions; runs being torn down concurrently with reads.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

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

      AbortSignal.timeout(this.options.storageTimeoutMs ?? 5_000),
    ]);
    let object: Awaited<ReturnType<StorageService["getObject"]>> | undefined;
    const aborted = new Promise<never>((_, reject) => {
      signal.addEventListener(
        "abort",
        () => {
          object?.stream.destroy();
          reject(new Error("paperclip_runner_chat_attachment_read_aborted"));
        },
        { once: true },
      );
    });
    const acquiring = storage
      .getObject(this.options.binding.companyId, source.objectKey)
      .then((value) => {
        if (signal.aborted) {
          value.stream.destroy();
          throw new Error("paperclip_runner_chat_attachment_read_aborted");
        }
        object = value;
        return value;
      });
    const read = (async () => {
      const value = await acquiring;
      const chunks: Buffer[] = [];
      let length = 0;
      try {
        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);
        }

View on GitHub (pinned to 3f1d897a7c)