{"record":{"id":"e408884a41fd0045","repo":"KeygraphHQ/shannon","slug":"failed-to-write-final-report-err-message","errorCode":null,"errorMessage":"Failed to write final report: ${err.message}","messagePattern":"Failed to write final report: (.+?)","errorType":"exception","errorClass":"PentestError","httpStatus":null,"severity":"error","filePath":"apps/worker/src/services/reporting.ts","lineNumber":86,"sourceCode":"          false,\n          { deliverableFile: file.paths, sourceDir },\n          ErrorCode.DELIVERABLE_NOT_FOUND,\n        );\n      }\n      logger.info(`No ${file.name} deliverable found`);\n    }\n  }\n\n  const finalContent = sections.join('\\n\\n');\n  const finalReportPath = path.join(dir, ASSEMBLED_REPORT_FILENAME);\n\n  try {\n    await fs.ensureDir(dir);\n    await fs.writeFile(finalReportPath, finalContent);\n    logger.info(`Final report assembled at ${finalReportPath}`);\n  } catch (error) {\n    const err = error as Error;\n    throw new PentestError(`Failed to write final report: ${err.message}`, 'filesystem', false, {\n      finalReportPath,\n      originalError: err.message,\n    });\n  }\n\n  return finalContent;\n}\n\n/**\n * Inject model information into the final security report.\n * Reads session.json to get the model(s) used, then injects a \"Model:\" line\n * into the Executive Summary section of the report.\n */\nexport async function injectModelIntoReport(\n  repoPath: string,\n  deliverablesSubdir: string | undefined,\n  outputPath: string,\n  logger: ActivityLogger,","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/KeygraphHQ/shannon/blob/1ae0a142f8525410a688f0309fd003cc5b1d92de/apps/worker/src/services/reporting.ts#L68-L104","documentation":"Thrown by assembleFinalReport when fs.ensureDir or fs.writeFile fails while writing the assembled markdown report to <dir>/<ASSEMBLED_REPORT_FILENAME>. The underlying error (ENOSPC, EACCES, EROFS, EMFILE, etc.) is captured in context.originalError. Category 'filesystem', non-retryable. This is a raw OS/filesystem write failure on the final assembled-report artifact.","triggerScenarios":"The worker container's deliverables volume is out of disk space (ENOSPC), the target directory is read-only (EROFS) or lacks write permission (EACCES), the process has exhausted file descriptors (EMFILE), or the path is on a disconnected/unmounted bind mount.","commonSituations":"Docker volume or overlay filesystem filled by large agent logs/deliverables. The repo path mounted read-only. Container runs as a UID without write permission to the deliverables dir. A full tmpfs backing the workspace.","solutions":["Read context.originalError and context.finalReportPath on the thrown PentestError to get the OS error code and target path.","Free disk space on the volume backing the deliverables dir (docker system prune, remove old workspaces).","Verify the worker container has write permission (correct UID) and the mount is not read-only for the deliverables path.","Raise the file-descriptor limit (ulimit -n) if EMFILE.","Re-run the scan or the report activity after fixing the filesystem condition; prior deliverables are preserved so only assembly is retried."],"exampleFix":"// before: deliverables volume full (ENOSPC)\n//   docker run -v repo:/repo ... worker  -> 'Failed to write final report: ENOSPC ...'\n// after: reclaim space and resume\n//   docker system prune -f; rm -rf ./workspaces/<old-workspace>\n//   ./shannon start -u <url> -r <repo> -w <same-workspace>","handlingStrategy":"try-catch","validationCode":"// Before report assembly, assert the dir is writable and disk has space\nimport { df } from 'node:fs/promises'; // pseudo\nconst dir = deliverablesDir(sourceDir, deliverablesSubdir);\nawait fs.ensureDir(dir);\nconst probe = path.join(dir, '.write-probe');\nawait fs.writeFile(probe, ''); await fs.remove(probe);\n// optionally check free space via statfs if available","typeGuard":"function isWriteError(e: unknown): boolean {\n  return e instanceof Error && /ENOSPC|EACCES|EROFS|EMFILE|ENOSPC/.test(e.message);\n}","tryCatchPattern":"try {\n  await assembleFinalReport(sourceDir, deliverablesSubdir, logger);\n} catch (e) {\n  if (e instanceof PentestError && /Failed to write final report/.test(e.message)) {\n    const code = (e.context as any)?.originalError;\n    if (/ENOSPC/.test(code)) { /* free disk, then retry assembly — deliverables preserved */ }\n    if (/EACCES|EROFS/.test(code)) { /* fix mount/permissions */ }\n  }\n  throw e;\n}","preventionTips":["Monitor disk usage on the volume backing workspaces; prune old workspaces regularly.","Run the worker container with a UID that has write permission to the deliverables dir.","Mount the repo read-write, never read-only, for report-capable scans.","Raise ulimit -n if the scan opens many deliverable files concurrently."],"tags":["reporting","filesystem","disk-full","permissions","deliverables"],"backgroundTag":null,"analyzedSha":"1ae0a142f8525410a688f0309fd003cc5b1d92de","analyzedAt":"2026-08-12T17:40:03.583Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}