{"record":{"id":"f3c9bf73442f86d6","repo":"can1357/oh-my-pi","slug":"no-relevant-files-to-summarize-after-filtering","errorCode":null,"errorMessage":"No relevant files to summarize after filtering","messagePattern":"No relevant files to summarize after filtering","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/commit/conventional/map-reduce.ts","lineNumber":64,"sourceCode":"}\n\n/** Group non-binary file indices into greedy LLM batches. */\nexport function buildLlmFileBatches(files: readonly ConventionalFileDiff[], budget: number): number[][] {\n\tconst indices: number[] = [];\n\tfor (let index = 0; index < files.length; index += 1) if (!files[index]?.isBinary) indices.push(index);\n\treturn buildBatchesForIndices(files, indices, budget);\n}\n\n/** Run exact per-file observation mapping followed by one reduce synthesis call. */\nexport async function runMapReduce(input: {\n\tinference: CommitInference;\n\tconfig: ConventionalGenerationConfig;\n\tstat: string;\n\tdiff: string;\n\tscopeCandidates: string;\n}): Promise<ConventionalAnalysis> {\n\tconst files = includedFiles(parsePromptDiff(input.diff), input.config);\n\tif (files.length === 0) throw new Error(\"No relevant files to summarize after filtering\");\n\tconst observations = await mapPhase(files, input.inference, input.config);\n\tconst prompts = renderConventionalPrompt(\"reduce\", {\n\t\ttypes_description: formatTypesDescription(),\n\t\tobservations: renderObservationsMarkdown(observations),\n\t\tstat: condenseStat(input.stat),\n\t\tscope_candidates: input.scopeCandidates,\n\t});\n\treturn input.inference.complete(\n\t\t{\n\t\t\toperation: \"map-reduce/reduce\",\n\t\t\trole: \"analysis\",\n\t\t\tpromptFamily: \"reduce\",\n\t\t\tsystemPrompt: prompts.system,\n\t\t\tuserPrompt: prompts.user,\n\t\t\ttoolName: \"create_conventional_analysis\",\n\t\t\tprogressLabel: \"Reducing file observations…\",\n\t\t},\n\t\tresponse => parseConventionalAnalysisMarkdown(response.text),","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/commit/conventional/map-reduce.ts#L46-L82","documentation":"The map-reduce analysis pipeline filters the diff down to files relevant for the commit analysis (via includedFiles over the parsed prompt diff). If no files survive that filter, there is nothing for the map phase to summarize, so runMapReduce throws instead of producing an empty analysis.","triggerScenarios":"Calling runMapReduce (directly or via analysis/generateAnalysisWithMapReduce) when parsePromptDiff(input.diff) yields files that are all excluded by the config's file filter — e.g. the diff contains only ignored paths (lockfiles, generated files, binary files) or the diff is malformed so no filenames parse out.","commonSituations":"Commits touching only package-lock.json / bun.lock / generated artifacts, config's relevant-file globs too restrictive, passing a truncated or context-only diff with no file headers, or a custom ConventionalGenerationConfig whose filter excludes everything.","solutions":["Broaden the file filter patterns in the generation config so the changed files qualify","Stage actual source changes, not only lockfiles/generated files","Pass the full diff text (verify it contains 'diff --git' file headers)","Fall back to direct (non-map-reduce) analysis for filter-only commits"],"exampleFix":"// before\nconst analysis = await runMapReduce({ diff, config });\n// after: guard with fallback\nconst files = includedFiles(parsePromptDiff(diff), config);\nconst analysis = files.length > 0\n  ? await runMapReduce({ diff, config })\n  : await generateDirectAnalysis({ diff, config });","handlingStrategy":"validation","validationCode":"const files = includedFiles(parsePromptDiff(diff), config);\nif (files.length === 0) {\n  // fall back to direct analysis or abort with a clear message\n}","typeGuard":"function hasMapReduceInput(diff: string, config: Config): boolean {\n  return includedFiles(parsePromptDiff(diff), config).length > 0;\n}","tryCatchPattern":"try {\n  return await runMapReduce(input);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"No relevant files\")) {\n    return generateDirectAnalysis(input); // fallback path\n  }\n  throw err;\n}","preventionTips":["Keep file-filter globs permissive enough for your repo's source layout","Check git status/diff before invoking generation","Fall back to direct analysis when the diff is filter-only (lockfiles, generated code)"],"tags":["map-reduce","filtering","diff"],"backgroundTag":"no-relevant-files","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}