{"record":{"id":"59f2733f0df2aab0","repo":"can1357/oh-my-pi","slug":"not-a-file-shortenpath-resolved","errorCode":null,"errorMessage":"Not a file: ${shortenPath(resolved)}","messagePattern":"Not a file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/compress/index.ts","lineNumber":70,"sourceCode":" * fewer files than asked is worse than failing.\n */\nexport async function resolveCompressTargets(patterns: readonly string[], cwd: string): Promise<string[]> {\n\tconst found = new Set<string>();\n\tfor (const pattern of patterns) {\n\t\tif (/[*?[\\]{}]/.test(pattern)) {\n\t\t\t// `dot: true` — prompt corpora live under dot directories such as `.omp/commands`.\n\t\t\tconst matches = new Bun.Glob(pattern).scanSync({ cwd, absolute: true, onlyFiles: true, dot: true });\n\t\t\tlet matched = 0;\n\t\t\tfor (const match of matches) {\n\t\t\t\tfound.add(match);\n\t\t\t\tmatched += 1;\n\t\t\t}\n\t\t\tif (matched === 0) throw new Error(`No files matched \"${pattern}\"`);\n\t\t\tcontinue;\n\t\t}\n\t\tconst resolved = path.resolve(cwd, pattern);\n\t\tconst stat = await fs.stat(resolved).catch(() => undefined);\n\t\tif (!stat?.isFile()) throw new Error(`Not a file: ${shortenPath(resolved)}`);\n\t\tfound.add(resolved);\n\t}\n\treturn [...found].sort();\n}\n\n/** Compress every requested file through the rewrite/approve loop. */\nexport async function runCompressCommand(options: CompressCommandOptions): Promise<CompressResult> {\n\tconst maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;\n\tconst concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;\n\tif (!Number.isInteger(maxRounds) || maxRounds <= 0) throw new Error(\"--rounds must be a positive integer\");\n\tif (!Number.isInteger(concurrency) || concurrency <= 0) throw new Error(\"--agents must be a positive integer\");\n\tif (options.inPlace && options.output) throw new Error(\"--in-place and --out are mutually exclusive\");\n\t// Paths and patterns follow the shell's cwd, as a file-taking CLI must; the project\n\t// dir only scopes settings discovery for the sessions.\n\tconst invocationDir = process.cwd();\n\tconst cwd = getProjectDir();\n\tconst targets = await resolveCompressTargets(options.files, invocationDir);\n\tif (targets.length === 0) throw new Error(\"No files to compress\");","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/compress/index.ts#L52-L88","documentation":"resolveCompressTargets resolves non-glob arguments to literal paths via path.resolve + fs.stat. If the path does not exist or is not a regular file (directory, socket, symlink-to-dir), it throws with the shortened path. Only plain files can go through the compress rewrite/approve loop.","triggerScenarios":"Passing a directory (`omp compress src/`), a nonexistent path, a path that exists relative to a different cwd than expected, or a special file; stat fails (ENOENT/permission) and `stat?.isFile()` is falsy.","commonSituations":"Passing a directory expecting recursive compression; typo in filename; running from the wrong directory so relative path resolves elsewhere; deleted/renamed file since last command; permission-denied path silently treated as missing.","solutions":["Verify the path exists with `ls <path>` and that it is a file, not a directory.","cd to the intended directory or pass an absolute path (paths resolve against the shell cwd).","To compress many files use a glob plus --in-place instead of a directory.","Fix typos or re-create the missing file."],"exampleFix":"// before\nomp compress src/        // Not a file: src\n// after\nomp compress 'src/**/*.ts' --in-place","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\nasync function isFileArg(p: string): Promise<boolean> {\n  try { return (await fs.stat(p)).isFile(); } catch { return false; }\n}\n// filter args: for (const a of args) if (!(await isFileArg(a))) console.error(`skipping ${a}`);","typeGuard":null,"tryCatchPattern":"try {\n  await runCompressCommand({ files: [target] });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Not a file:\")) {\n    process.stderr.write(`${err.message} — pass a regular file, not a directory\\n`);\n  } else throw err;\n}","preventionTips":["Pass explicit regular-file paths; use globs + --in-place for directories.","Run from the directory where relative paths resolve as intended.","Check that referenced files exist before scripting the call (test -f).","Remember permission-denied paths surface as 'not a file' — check permissions too."],"tags":["filesystem","path","cli","input-validation"],"backgroundTag":"path-is-not-a-file","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}