{"record":{"id":"170a0d2ccc6ea85a","repo":"can1357/oh-my-pi","slug":"no-files-to-compress","errorCode":null,"errorMessage":"No files to compress","messagePattern":"No files to compress","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/compress/index.ts","lineNumber":88,"sourceCode":"\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\");\n\tif (targets.length > 1 && !options.inPlace) {\n\t\tthrow new Error(`${targets.length} files matched; pass --in-place to rewrite them (--out takes a single file)`);\n\t}\n\n\tconst abortController = new AbortController();\n\tconst abort = (): void => abortController.abort(new Error(\"Compress interrupted\"));\n\tprocess.once(\"SIGINT\", abort);\n\tprocess.once(\"SIGTERM\", abort);\n\tconst progress = createProgressReporter(\"Compressing\");\n\tconst emitToStdout = targets.length === 1 && !options.inPlace && options.output === undefined;\n\n\ttry {\n\t\tconsole.error(`Compressing ${targets.length} file(s)${options.model ? ` with ${options.model}` : \"\"}`);\n\t\tprogress.start(targets.length);\n\t\tconst settled = await mapWithConcurrencyLimitAllSettled(\n\t\t\ttargets,\n\t\t\tMath.min(concurrency, targets.length),\n\t\t\tasync (target, index, signal) => {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/compress/index.ts#L70-L106","documentation":"After resolving targets, runCompressCommand throws if the resolved target list is empty. This happens when options.files is an empty array (or omitted with no positional file arguments), so there is nothing to compress. Unlike the per-pattern error, no input was given at all.","triggerScenarios":"Calling runCompressCommand({ files: [] }) programmatically, or the CLI wiring passing no positional file arguments to the compress command.","commonSituations":"Wrapper scripts where `$@` expanded to nothing (no files passed); CI jobs whose file list came from an empty diff/glob expansion; forgetting the positional file arguments entirely.","solutions":["Pass at least one file or glob: `omp compress src/foo.ts`.","In scripts, guard before invoking: `[ ${#FILES[@]} -gt 0 ] || exit 1`.","Check whether the shell pre-expanded a glob into nothing and adjust quoting ('*.ts' to defer expansion)."],"exampleFix":"// before\nFILES=$(git diff --name-only HEAD~1)\nomp compress $FILES        // empty diff -> no args\n// after\n[ -n \"$FILES\" ] && omp compress $FILES || echo 'no changed files'","handlingStrategy":"validation","validationCode":"if (!files || files.length === 0) {\n  throw new Error(\"compress requires at least one file or glob\");\n}\n// check before calling runCompressCommand","typeGuard":"function hasTargets(files: string[] | undefined): files is [string, ...string[]] {\n  return Array.isArray(files) && files.length > 0;\n}","tryCatchPattern":"try {\n  await runCompressCommand({ files });\n} catch (err) {\n  if (err instanceof Error && err.message === \"No files to compress\") {\n    process.stderr.write(`${err.message} — pass file arguments\\n`);\n  } else throw err;\n}","preventionTips":["Always pass at least one file or glob to the compress command.","In shell scripts, quote globs and check expansion (`${FILES:-}`) before invoking.","In CI, skip the step when the changed-file list is empty instead of calling with [] .","Assert non-empty args in wrappers around programmatic invocations."],"tags":["cli","argument-validation","empty-input"],"backgroundTag":"missing-required-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}