{"record":{"id":"c8e61e7871faf793","repo":"garrytan/gstack","slug":"file-not-found","errorCode":null,"errorMessage":"File not found","messagePattern":"File not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/path-security.ts","lineNumber":109,"sourceCode":"    } else {\n      throw new Error(`Cannot resolve real path: ${filePath} (${err.code})`);\n    }\n  }\n  const isSafe = SAFE_DIRECTORIES.some(dir => isPathWithin(realPath, dir));\n  if (!isSafe) {\n    throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);\n  }\n}\n\n/** Validate a file path for remote serving (GET /file). TEMP_DIR only, not cwd. */\nexport function validateTempPath(filePath: string): void {\n  const resolved = path.resolve(filePath);\n  let realPath: string;\n  try {\n    realPath = fs.realpathSync(resolved);\n  } catch (err: any) {\n    if (err.code === 'ENOENT') {\n      throw new Error('File not found');\n    }\n    throw new Error(`Cannot resolve path: ${filePath}`);\n  }\n  const isSafe = TEMP_ONLY.some(dir => isPathWithin(realPath, dir));\n  if (!isSafe) {\n    throw new Error(`Path must be within: ${TEMP_ONLY.join(', ')} (remote file serving is restricted to temp directory)`);\n  }\n}\n\n/** Escape special regex metacharacters in a user-supplied string to prevent ReDoS. */\nexport function escapeRegExp(s: string): string {\n  return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","sourceCodeStart":91,"sourceCodeEnd":123,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/path-security.ts#L91-L123","documentation":"Thrown by validateTempPath (the GET /file remote-serving validator) when realpathSync fails with ENOENT — the file simply does not exist. This fires before any safety check, giving a clear 'File not found' to the remote agent rather than a confusing sandbox error.","triggerScenarios":"A remote agent requests GET /file?path=<something> and the file does not exist in TEMP_DIR: it was never written, was already deleted by temp cleanup, or the filename is wrong.","commonSituations":"Screenshot/pdf not yet generated when the remote agent fetches it; OS temp cleanup (systemd-tmpfiles) wiped the file between write and read; wrong filename in the request; race between writer and reader.","solutions":["Verify the file exists in TEMP_DIR before requesting it: `ls -la <TEMP_DIR>/<name>`","Regenerate the artifact (re-run the screenshot/pdf/download command) and fetch again","Check the exact filename and TEMP_DIR location — print TMPDIR/os.tmpdir() to confirm","Fetch immediately after generation to avoid temp cleanup windows"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import * as fs from 'fs';\n\nfunction tempFileExists(p: string): boolean {\n  try { fs.accessSync(p, fs.constants.R_OK); return true; }\n  catch { return false; }\n}\n\nif (!tempFileExists(filePath)) {\n  return { status: 404, body: 'File not found — generate it first' };\n}","typeGuard":"function isExistingTempFile(p: string): boolean {\n  try { const s = fs.statSync(p); return s.isFile(); } catch { return false; }\n}","tryCatchPattern":"try {\n  return serveTempFile(filePath);\n} catch (e: any) {\n  if (e.message === 'File not found') return { status: 404, body: 'File not found' };\n  throw e;\n}","preventionTips":["Fetch the artifact immediately after generation to avoid temp cleanup windows","Have the generator return the exact filename and pass it through verbatim","Confirm os.tmpdir() matches between writer and reader processes","Log the file path at write time so the reader knows what to request"],"tags":["filesystem","not-found","remote-serving","enoent","temp-dir"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}