{"record":{"id":"77d0e6f222378dd7","repo":"jackwener/OpenCLI","slug":"failed-to-inspect-trace-artifact-dir-err-ins","errorCode":null,"errorMessage":"Failed to inspect trace artifact ${dir}: ${err instanceof Error ? err.message : String(err)}","messagePattern":"Failed to inspect trace artifact (.+?): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/observation/retention.ts","lineNumber":169,"sourceCode":"    warn(`Failed to list trace artifacts in ${tracesDir}: ${err instanceof Error ? err.message : String(err)}`);\n    return [];\n  }\n\n  const entries: TraceEntry[] = [];\n  for (const name of names) {\n    const dir = path.join(tracesDir, name);\n    try {\n      const stat = fs.statSync(dir);\n      if (!stat.isDirectory()) continue;\n      entries.push({\n        dir,\n        createdAtMs: readCreatedAtMs(dir, stat.mtimeMs),\n        sizeBytes: directorySize(dir),\n        protected: protectedDirs.has(path.resolve(dir)),\n      });\n    } catch (err) {\n      if (!isEnoent(err)) {\n        warn(`Failed to inspect trace artifact ${dir}: ${err instanceof Error ? err.message : String(err)}`);\n      }\n    }\n  }\n  return entries;\n}\n\nfunction readCreatedAtMs(dir: string, fallbackMs: number): number {\n  try {\n    const receipt = JSON.parse(fs.readFileSync(path.join(dir, 'receipt.json'), 'utf-8')) as { createdAt?: unknown };\n    if (typeof receipt.createdAt === 'string') {\n      const parsed = Date.parse(receipt.createdAt);\n      if (Number.isFinite(parsed)) return parsed;\n    }\n  } catch {\n    // Older or hand-edited trace directories may not have a receipt.\n  }\n  return fallbackMs;\n}","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/observation/retention.ts#L151-L187","documentation":"While building TraceEntry metadata, readTraceEntries stats each trace directory (createdAt, size, protected flags). If inspection of one directory fails with a non-ENOENT error, this warning is emitted and that directory is skipped from the entries list, but the loop continues for remaining directories.","triggerScenarios":"The per-entry stat/size walk (readCreatedAtMs, directorySize) throws — e.g. the directory vanished between readdir and stat (ENOENT is silently ignored, others are not), permission denied on a subdirectory during directorySize traversal, or symlink loops causing ELOOP.","commonSituations":"Another process (or a concurrent prune) deleting trace dirs mid-scan; partially-written traces with restricted subdirectories; symlinked trace dirs pointing outside with no access; ELOOP from cyclic symlinks.","solutions":["Re-run the retention pass — most cases are a race with deletion and resolve on retry.","Check permissions on the specific directory (path is included in the message) and fix with chmod/chown.","Remove or fix broken/cyclic symlinks inside the traces directory.","Ensure no concurrent process is mutating the traces directory while retention runs (serialize cleanup)."],"exampleFix":"// before\ndirectorySize(dir); // throws on unreadable subdir\n// after\nfunction safeDirectorySize(dir: string): number {\n  try { return directorySize(dir); } catch { return 0; }\n}","handlingStrategy":"try-catch","validationCode":"try { fs.accessSync(dir, fs.constants.R_OK); } catch { console.warn(`unreadable trace dir, skipping: ${dir}`); }","typeGuard":"function isStatSafe(dir: string): boolean {\n  try { return fs.statSync(dir).isDirectory(); } catch { return false; }\n}","tryCatchPattern":"try {\n  const stat = fs.statSync(dir);\n  inspect(dir, stat);\n} catch (err) {\n  if ((err as NodeJS.ErrnoException).code !== 'ENOENT') console.warn(`inspect failed ${dir}: ${(err as Error).message}`);\n}","preventionTips":["Avoid concurrent prune + scan over the same traces directory.","Remove cyclic symlinks from trace output.","Keep uniform permissions across trace subdirectories.","Treat missing entries (ENOENT) as normal and retry the pass later."],"tags":["filesystem","race-condition","permissions"],"backgroundTag":"stat-failed-enoent","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}