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

  1. Include a distinctive identifier, name, or statement in the pattern (e.g. the function name before the closing brace).
  2. Add more surrounding lines so the anchor contains identifier text.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/257c5f27faa82655. Report an issue: GitHub.