can1357/oh-my-pi · error
Not a file: ${shortenPath(resolved)}
Error message
Not a file: ${shortenPath(resolved)} What it means
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.
Source
Thrown at packages/coding-agent/src/compress/index.ts:70
* fewer files than asked is worse than failing.
*/
export async function resolveCompressTargets(patterns: readonly string[], cwd: string): Promise<string[]> {
const found = new Set<string>();
for (const pattern of patterns) {
if (/[*?[\]{}]/.test(pattern)) {
// `dot: true` — prompt corpora live under dot directories such as `.omp/commands`.
const matches = new Bun.Glob(pattern).scanSync({ cwd, absolute: true, onlyFiles: true, dot: true });
let matched = 0;
for (const match of matches) {
found.add(match);
matched += 1;
}
if (matched === 0) throw new Error(`No files matched "${pattern}"`);
continue;
}
const resolved = path.resolve(cwd, pattern);
const stat = await fs.stat(resolved).catch(() => undefined);
if (!stat?.isFile()) throw new Error(`Not a file: ${shortenPath(resolved)}`);
found.add(resolved);
}
return [...found].sort();
}
/** Compress every requested file through the rewrite/approve loop. */
export async function runCompressCommand(options: CompressCommandOptions): Promise<CompressResult> {
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
if (!Number.isInteger(maxRounds) || maxRounds <= 0) throw new Error("--rounds must be a positive integer");
if (!Number.isInteger(concurrency) || concurrency <= 0) throw new Error("--agents must be a positive integer");
if (options.inPlace && options.output) throw new Error("--in-place and --out are mutually exclusive");
// Paths and patterns follow the shell's cwd, as a file-taking CLI must; the project
// dir only scopes settings discovery for the sessions.
const invocationDir = process.cwd();
const cwd = getProjectDir();
const targets = await resolveCompressTargets(options.files, invocationDir);
if (targets.length === 0) throw new Error("No files to compress");View on GitHub (pinned to 9690622007)
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.
Example fix
// before omp compress src/ // Not a file: src // after omp compress 'src/**/*.ts' --in-place
Defensive patterns
Strategy: validation
Validate before calling
import * as fs from "node:fs/promises";
async function isFileArg(p: string): Promise<boolean> {
try { return (await fs.stat(p)).isFile(); } catch { return false; }
}
// filter args: for (const a of args) if (!(await isFileArg(a))) console.error(`skipping ${a}`); Try / catch
try {
await runCompressCommand({ files: [target] });
} catch (err) {
if (err instanceof Error && err.message.startsWith("Not a file:")) {
process.stderr.write(`${err.message} — pass a regular file, not a directory\n`);
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Import source is neither file nor directory: ${target}
- No files matched "${pattern}"
- cannot access {}: Not a directory
- invalid template, {}; with --tmpdir, it may not be absolute
- failed to access {0}: Not a directory
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/59f2733f0df2aab0.
Report an issue: GitHub.