{"record":{"id":"7f316fb0b7888f37","repo":"coleam00/Archon","slug":"failed-to-save-uploaded-file-check-available-disk","errorCode":null,"errorMessage":"Failed to save uploaded file. Check available disk space.","messagePattern":"Failed to save uploaded file\\. Check available disk space\\.","errorType":"http","errorClass":null,"httpStatus":500,"severity":"critical","filePath":"packages/server/src/routes/api.ts","lineNumber":2389,"sourceCode":"          path: filePath,\n          name: safeName || fileId,\n          mimeType: normalizedMime,\n          size: entry.size,\n        });\n      }\n    } catch (writeErr: unknown) {\n      for (const f of savedFiles) {\n        await unlink(f.path).catch((err: NodeJS.ErrnoException) => {\n          if (err.code !== 'ENOENT') {\n            getLog().warn({ err, filePath: f.path, conversationId }, 'upload.rollback_failed');\n          }\n        });\n      }\n      getLog().error({ err: writeErr, conversationId }, 'upload.write_failed');\n      return {\n        ok: false,\n        status: 500,\n        error: 'Failed to save uploaded file. Check available disk space.',\n      };\n    }\n\n    return { ok: true, savedFiles, uploadDir };\n  }\n\n  async function dispatchToOrchestrator(\n    conversationId: string,\n    message: string,\n    extraContext?: Omit<HandleMessageContext, 'isolationHints'>,\n    filesToCleanup?: { files: AttachedFile[]; uploadDir: string }\n  ): Promise<{ accepted: boolean; status: string }> {\n    const result = await lockManager.acquireLock(conversationId, async () => {\n      // Emit lock:true at handler start so the UI knows processing has begun.\n      // Fire-and-forget — if no SSE stream is connected yet, the event is buffered.\n      webAdapter.emitLockEvent(conversationId, true);\n      try {\n        await handleMessage(webAdapter, conversationId, message, {","sourceCodeStart":2371,"sourceCodeEnd":2407,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/server/src/routes/api.ts#L2371-L2407","documentation":"Returned when writing an uploaded file to the conversation's upload directory fails (writeErr caught). The server logs upload.write_failed with the error and conversationId, then responds 500 with this message directing the operator at disk space, since the most common cause of an fs write failure during upload is an exhausted or failing filesystem.","triggerScenarios":"POSTing multipart uploads to the conversation message route when the write of a file to the conversation uploadDir fails: disk full, inode exhaustion, permission denied on the upload directory, quota exceeded, or I/O error on the backing device.","commonSituations":"Self-hosted instance running on a small volume that filled up with run artifacts/logs; Docker container hitting a disk quota; read-only remount after disk errors; upload dir owned by a different user after a manual fix.","solutions":["Free disk space on the volume holding the upload directory (df -h, clean old run artifacts/logs).","Check the server log entry upload.write_failed for the exact fs error (ENOSPC vs EACCES vs EIO).","Verify the upload directory exists and is writable by the server process (permissions/ownership).","Retry the upload once space/permissions are fixed."],"exampleFix":"// before: blind retry after failure\nawait upload(file); // 500\n// after: check capacity first\nconst stat = fs.statfsSync(uploadRoot);\nif (stat.bavail * stat.bsize < file.size * 2) throw new Error('Insufficient disk space');\nawait upload(file);","handlingStrategy":"validation","validationCode":"import { statfsSync } from 'node:fs';\nfunction hasSpaceFor(dir: string, bytes: number): boolean {\n  const s = statfsSync(dir);\n  return s.bavail * s.bsize > bytes * 2; // headroom factor\n}\nif (!hasSpaceFor(uploadDir, file.size)) throw new Error('Insufficient disk space for upload');","typeGuard":"function isUploadSaveFailure(res: { status: number; error?: string }): res is { status: 500; error: 'Failed to save uploaded file. Check available disk space.' } {\n  return res.status === 500 && /Failed to save uploaded file/.test(res.error ?? '');\n}","tryCatchPattern":"try {\n  await uploadFile(convId, file);\n} catch (err) {\n  if (isUploadSaveFailure(err)) {\n    // do not blind-retry; free space / fix permissions first\n    const free = statfsSync(uploadDir).bavail * statfsSync(uploadDir).bsize;\n    throw new Error(`Upload write failed; ${free} bytes free on upload volume`, { cause: err });\n  }\n  throw err;\n}","preventionTips":["Alert on disk usage for the volume holding Archon run artifacts and upload dirs (e.g. >80%).","Prune old run artifacts/logs on a schedule so uploads never hit a full disk.","Verify upload directory ownership/permissions after any manual maintenance.","Check the upload.write_failed log entry to distinguish ENOSPC from EACCES/EIO before fixing."],"tags":["filesystem","disk-space","upload","http-500"],"backgroundTag":"disk-full","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}