jackwener/OpenCLI · warning

Failed to inspect trace artifact ${dir}: ${err instanceof Er

Error message

Failed to inspect trace artifact ${dir}: ${err instanceof Error ? err.message : String(err)}

What it means

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.

Source

Thrown at src/observation/retention.ts:169

    warn(`Failed to list trace artifacts in ${tracesDir}: ${err instanceof Error ? err.message : String(err)}`);
    return [];
  }

  const entries: TraceEntry[] = [];
  for (const name of names) {
    const dir = path.join(tracesDir, name);
    try {
      const stat = fs.statSync(dir);
      if (!stat.isDirectory()) continue;
      entries.push({
        dir,
        createdAtMs: readCreatedAtMs(dir, stat.mtimeMs),
        sizeBytes: directorySize(dir),
        protected: protectedDirs.has(path.resolve(dir)),
      });
    } catch (err) {
      if (!isEnoent(err)) {
        warn(`Failed to inspect trace artifact ${dir}: ${err instanceof Error ? err.message : String(err)}`);
      }
    }
  }
  return entries;
}

function readCreatedAtMs(dir: string, fallbackMs: number): number {
  try {
    const receipt = JSON.parse(fs.readFileSync(path.join(dir, 'receipt.json'), 'utf-8')) as { createdAt?: unknown };
    if (typeof receipt.createdAt === 'string') {
      const parsed = Date.parse(receipt.createdAt);
      if (Number.isFinite(parsed)) return parsed;
    }
  } catch {
    // Older or hand-edited trace directories may not have a receipt.
  }
  return fallbackMs;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the retention pass — most cases are a race with deletion and resolve on retry.
  2. Check permissions on the specific directory (path is included in the message) and fix with chmod/chown.
  3. Remove or fix broken/cyclic symlinks inside the traces directory.
  4. Ensure no concurrent process is mutating the traces directory while retention runs (serialize cleanup).

Example fix

// before
directorySize(dir); // throws on unreadable subdir
// after
function safeDirectorySize(dir: string): number {
  try { return directorySize(dir); } catch { return 0; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

try { fs.accessSync(dir, fs.constants.R_OK); } catch { console.warn(`unreadable trace dir, skipping: ${dir}`); }

Type guard

function isStatSafe(dir: string): boolean {
  try { return fs.statSync(dir).isDirectory(); } catch { return false; }
}

Try / catch

try {
  const stat = fs.statSync(dir);
  inspect(dir, stat);
} catch (err) {
  if ((err as NodeJS.ErrnoException).code !== 'ENOENT') console.warn(`inspect failed ${dir}: ${(err as Error).message}`);
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/77d0e6f222378dd7. Report an issue: GitHub.