can1357/oh-my-pi · error
Operation ${operationNumber} pattern is too generic; include
Error message
Operation ${operationNumber} pattern is too generic; include a distinctive name or statement. What it means
The pattern has literal tokens, but every literal is punctuation-only (no letters, digits, `_`, or `$`). Punctuation-only anchors like `}`, `};`, or `);` match nearly everywhere in a source file, so the library refuses the pattern as too generic and asks for a distinctive name or statement. Identifier text — however short — is accepted; only punctuation is rejected.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:1573
let strippedLeading = 0;
while (tokens[0]?.kind === "gap") {
tokens.shift();
strippedLeading++;
}
while (tokens.at(-1)?.kind === "gap") tokens.pop();
for (let index = 0; index < selectionBoundaries.length; index++) {
selectionBoundaries[index] = Math.max(0, Math.min(tokens.length, selectionBoundaries[index] - strippedLeading));
}
const literals = tokens.filter((token): token is LiteralToken => token.kind === "literal");
if (literals.length === 0) throw new Error(`Operation ${operationNumber} needs visible current text.`);
// Only punctuation-only anchors (`}`, `};`, `);`) are genuinely too generic:
// they match everywhere and their candidate lists are noise. Any identifier
// text — however short (`id`, `avlue`) — is a legitimate anchor; uniqueness
// (or an explicit `«*`) decides whether it applies, not its length.
const hasIdentifierText = literals.some(token => /[\p{L}\p{N}_$]/u.test(token.normalized));
if (!hasIdentifierText) {
throw new Error(`Operation ${operationNumber} pattern is too generic; include a distinctive name or statement.`);
}
const emptyDoubleSelection = selectionBoundaries.length === 2 && selectionBoundaries[0] === selectionBoundaries[1];
const insertion = selectionBoundaries.length === 1 || emptyDoubleSelection;
const explicitSingleSelection = selectionBoundaries.length === 2 && !emptyDoubleSelection;
const selectionStart = insertion || explicitSingleSelection ? selectionBoundaries[0] : 0;
const selectionEnd = insertion ? selectionStart : explicitSingleSelection ? selectionBoundaries[1] : tokens.length;
const selectionPairs =
selectionBoundaries.length > 0 && selectionBoundaries.length % 2 === 0
? Array.from({ length: selectionBoundaries.length / 2 }, (_, index) => {
const start = selectionBoundaries[index * 2];
const end = selectionBoundaries[index * 2 + 1];
return {
start,
end,
captureIndices: tokens
.slice(start, end)
.filter((token): token is GapToken => token.kind === "gap")View on GitHub (pinned to 9690622007)
Solutions
- Include a distinctive identifier, name, or statement in the pattern (e.g. the function name before the closing brace).
- Add more surrounding lines so the anchor contains identifier text.
- If genuinely intending 'everywhere', check whether the tool offers an explicit unambiguous mode (`«*`) rather than a generic punctuation anchor.
Example fix
// before
const pattern = "...\n};";
// after
const pattern = "export function render() {\n...\n};"; Defensive patterns
Strategy: validation
Validate before calling
const literals = pattern.replace(/\.\.\./g, "").trim();
if (!/[\p{L}\p{N}_$]/u.test(literals)) {
throw new Error("pattern is punctuation-only; include a distinctive name or statement");
} Type guard
function hasIdentifierAnchor(pattern: string): boolean {
return /[\p{L}\p{N}_$]/u.test(pattern.replace(/«|»|\.\.\./g, ""));
} Try / catch
try {
await applySloppyEdit({ pattern });
} catch (err) {
if (err instanceof Error && err.message.includes("too generic")) {
// widen pattern to include the named declaration the punctuation belongs to
}
throw err;
} Prevention
- Never anchor on bare closing punctuation (`}`, `};`, `);`).
- Include the function/class/variable name that owns the block you are editing.
- Remember: identifier text of any length counts; punctuation alone never does.
When it happens
Trigger: Building an operation whose pattern is just `}`, `};`, `);`, or similar closing punctuation with a gap, e.g. `...\n};`. Detected via `literals.some(token => /[\p{L}\p{N}_$]/u.test(token.normalized))` returning false.
Common situations: Targeting a block's closing brace to insert before/after it without naming the block; minified or template files where the author grabbed only delimiters; assuming the tool can disambiguate 'the last }' without context.
Related errors
- Operation ${operationNumber} has no visible current text.
- Operation ${operationNumber} has adjacent ${GAP}; use one el
- Operation ${operationNumber} needs visible current text.
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/257c5f27faa82655.
Report an issue: GitHub.