KeygraphHQ/shannon · error · PentestError

Failed to write final report: ${err.message}

Error message

Failed to write final report: ${err.message}

What it means

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.

Source

Thrown at apps/worker/src/services/reporting.ts:86

          false,
          { deliverableFile: file.paths, sourceDir },
          ErrorCode.DELIVERABLE_NOT_FOUND,
        );
      }
      logger.info(`No ${file.name} deliverable found`);
    }
  }

  const finalContent = sections.join('\n\n');
  const finalReportPath = path.join(dir, ASSEMBLED_REPORT_FILENAME);

  try {
    await fs.ensureDir(dir);
    await fs.writeFile(finalReportPath, finalContent);
    logger.info(`Final report assembled at ${finalReportPath}`);
  } catch (error) {
    const err = error as Error;
    throw new PentestError(`Failed to write final report: ${err.message}`, 'filesystem', false, {
      finalReportPath,
      originalError: err.message,
    });
  }

  return finalContent;
}

/**
 * Inject model information into the final security report.
 * Reads session.json to get the model(s) used, then injects a "Model:" line
 * into the Executive Summary section of the report.
 */
export async function injectModelIntoReport(
  repoPath: string,
  deliverablesSubdir: string | undefined,
  outputPath: string,
  logger: ActivityLogger,

View on GitHub (pinned to 1ae0a142f8)

Solutions

  1. Read context.originalError and context.finalReportPath on the thrown PentestError to get the OS error code and target path.
  2. Free disk space on the volume backing the deliverables dir (docker system prune, remove old workspaces).
  3. Verify the worker container has write permission (correct UID) and the mount is not read-only for the deliverables path.
  4. Raise the file-descriptor limit (ulimit -n) if EMFILE.
  5. Re-run the scan or the report activity after fixing the filesystem condition; prior deliverables are preserved so only assembly is retried.

Example fix

// before: deliverables volume full (ENOSPC)
//   docker run -v repo:/repo ... worker  -> 'Failed to write final report: ENOSPC ...'
// after: reclaim space and resume
//   docker system prune -f; rm -rf ./workspaces/<old-workspace>
//   ./shannon start -u <url> -r <repo> -w <same-workspace>
Defensive patterns

Strategy: try-catch

Validate before calling

// Before report assembly, assert the dir is writable and disk has space
import { df } from 'node:fs/promises'; // pseudo
const dir = deliverablesDir(sourceDir, deliverablesSubdir);
await fs.ensureDir(dir);
const probe = path.join(dir, '.write-probe');
await fs.writeFile(probe, ''); await fs.remove(probe);
// optionally check free space via statfs if available

Type guard

function isWriteError(e: unknown): boolean {
  return e instanceof Error && /ENOSPC|EACCES|EROFS|EMFILE|ENOSPC/.test(e.message);
}

Try / catch

try {
  await assembleFinalReport(sourceDir, deliverablesSubdir, logger);
} catch (e) {
  if (e instanceof PentestError && /Failed to write final report/.test(e.message)) {
    const code = (e.context as any)?.originalError;
    if (/ENOSPC/.test(code)) { /* free disk, then retry assembly — deliverables preserved */ }
    if (/EACCES|EROFS/.test(code)) { /* fix mount/permissions */ }
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of KeygraphHQ/shannon@1ae0a142f8 (2026-08-12). Data as JSON: /api/errors/e408884a41fd0045. Report an issue: GitHub.