{"record":{"id":"29a911120f55ce9c","repo":"paperclipai/paperclip","slug":"paperclip-runner-chat-attachment-read-cleanup-failed","errorCode":"paperclip_runner_chat_attachment_read_cleanup_failed","errorMessage":"paperclip_runner_chat_attachment_read_cleanup_failed","messagePattern":"paperclip_runner_chat_attachment_read_cleanup_failed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/chat-attachment-read.ts","lineNumber":269,"sourceCode":"        throw new Error(\n          \"paperclip_runner_chat_attachment_read_integrity_mismatch\",\n        );\n      return body;\n    })();\n    return Promise.race([read, aborted]);\n  }\n\n  close(): Promise<void> {\n    if (this.#closing) return this.#closing;\n    this.#closed = true;\n    this.#abort.abort();\n    this.#closing = (async () => {\n      await Promise.allSettled([...this.#pending]);\n      const results = await Promise.allSettled(\n        this.#cleanups.map((cleanup) => cleanup()),\n      );\n      if (results.some((result) => result.status === \"rejected\"))\n        throw new Error(\"paperclip_runner_chat_attachment_read_cleanup_failed\");\n    })();\n    return this.#closing;\n  }\n}\n","sourceCodeStart":251,"sourceCodeEnd":274,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/native-runtime/chat-attachment-read.ts#L251-L274","documentation":"Thrown by the native-runtime chat attachment read handle's close() when one or more registered cleanup callbacks reject during shutdown. close() first aborts in-flight reads, waits for pending work with Promise.allSettled, then runs each cleanup with allSettled; if any cleanup rejects, close() rejects with this sentinel error so callers know teardown was not fully clean. It aggregates any failure rather than surfacing the first underlying cleanup error directly.","triggerScenarios":"Calling close() on a ChatAttachmentRead handle while at least one registered cleanup function rejects — e.g. temp-file unlink failure, storage session teardown failure, or a cleanup throwing because its resource was already destroyed by the abort signal.","commonSituations":"Filesystem permission issues preventing temp attachment file deletion; a cleanup racing with the close-time abort so it tries to use an aborted/destroyed stream; an environment where the storage directory was removed mid-run; bugs in a custom cleanup callback.","solutions":["Inspect the Promise.allSettled rejection reasons by running the same cleanups individually or adding logging around each cleanup() to find the failing one.","Make each cleanup idempotent and tolerant of already-aborted/destroyed resources (catch NotFound/EBADF-style errors and swallow them).","Check filesystem permissions and disk state for the temp directory where attachments are staged.","Retry close() once — the implementation memoizes the closing promise, so a retry re-awaits the same settled result; if the error persists, fix the failing cleanup rather than retrying."],"exampleFix":"// before\ncleanup: async () => {\n  await fs.unlink(tempPath);\n}\n\n// after\ncleanup: async () => {\n  await fs.unlink(tempPath).catch((err: NodeJS.ErrnoException) => {\n    if (err.code !== \"ENOENT\") throw err;\n  });\n}","handlingStrategy":"try-catch","validationCode":"const staged = await fs.readdir(tempDir).catch(() => []);\nconst deletable = staged.every(f => fs.statSync(path.join(tempDir, f)).isFile());\nif (!deletable) console.warn(\"cleanup may fail: unexpected entries in\", tempDir);","typeGuard":"function isCleanupFailure(err: unknown): err is Error {\n  return err instanceof Error && err.message === \"paperclip_runner_chat_attachment_read_cleanup_failed\";\n}","tryCatchPattern":"try {\n  await handle.close();\n} catch (err) {\n  if (isCleanupFailure(err)) {\n    logger.warn(\"attachment read cleanup failed; leaking staged resources\", { cause: err });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Write cleanups that tolerate ENOENT/already-destroyed resources (idempotent teardown).","Never do fallible network or user-dependent work inside a close-time cleanup.","Log each cleanup's allSettled rejection reason in development to catch failing cleanups early.","Monitor for this error in production as a signal of leaked temp files or stuck storage sessions."],"tags":["cleanup","resource-lifecycle","shutdown","attachments"],"backgroundTag":"file-write-failed","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}