can1357/oh-my-pi · error

Symbol "${symbol}" occurrence ${occurrence} is out of bounds

Error message

Symbol "${symbol}" occurrence ${occurrence} is out of bounds on line ${lineNumber} (found ${fallbackIndexes.length})

What it means

After matching the requested symbol on the line, resolveSymbolColumn indexes into the match list with the 1-based occurrence from the `symbol#N` spec. If N exceeds the number of matches found (exact or fallback), this error reports how many occurrences actually exist.

Source

Thrown at packages/coding-agent/src/lsp/utils.ts:712

export async function resolveSymbolColumn(filePath: string, line: number, symbolSpec?: string): Promise<number> {
	const lineNumber = Math.max(1, line);
	try {
		const fileText = await Bun.file(filePath).text();
		const lines = fileText.split("\n");
		const targetLine = lines[lineNumber - 1] ?? "";
		if (!symbolSpec) {
			return firstNonWhitespaceColumn(targetLine);
		}

		const { symbol, occurrence } = parseSymbolSpec(symbolSpec);
		const exactIndexes = findSymbolMatchIndexes(targetLine, symbol);
		const fallbackIndexes = exactIndexes.length > 0 ? exactIndexes : findSymbolMatchIndexes(targetLine, symbol, true);
		if (fallbackIndexes.length === 0) {
			throw new Error(`Symbol "${symbol}" not found on line ${lineNumber}`);
		}
		if (occurrence > fallbackIndexes.length) {
			throw new Error(
				`Symbol "${symbol}" occurrence ${occurrence} is out of bounds on line ${lineNumber} (found ${fallbackIndexes.length})`,
			);
		}
		return fallbackIndexes[occurrence - 1];
	} catch (error) {
		if (isEnoent(error)) {
			throw new Error(`File not found: ${filePath}`);
		}
		throw error;
	}
}

export async function readLocationContext(filePath: string, line: number, contextLines = 1): Promise<string[]> {
	const targetLine = Math.max(1, line);
	const surrounding = Math.max(0, contextLines);
	try {
		const fileText = await Bun.file(filePath).text();
		const lines = fileText.split("\n");

View on GitHub (pinned to 9690622007)

Solutions

  1. Lower the occurrence number to one that exists (the error states the found count, e.g. found 2 → use #1 or #2).
  2. Re-read the line to count occurrences before specifying `#N`.
  3. Omit `#N` to use the first occurrence when ambiguity doesn't matter.

Example fix

// before
await lspTool.execute({ action: "rename", file: "a.ts", line: 7, symbol: "x#3", new_name: "y" }); // only 2 x's
// after
await lspTool.execute({ action: "rename", file: "a.ts", line: 7, symbol: "x#2", new_name: "y" });
Defensive patterns

Strategy: validation

Validate before calling

const text = (await Bun.file(file).text()).split("\n")[line - 1] ?? "";
const count = text.split(symbolBase).length - 1;
const occ = requestedOccurrence && requestedOccurrence <= count ? requestedOccurrence : 1;

Try / catch

try {
  return await lspTool.execute({ /* symbol: "x#3" */ });
} catch (err) {
  const m = err instanceof Error ? err.message.match(/found (\d+)/) : null;
  if (m) return lspTool.execute({ /* symbol: `x#${Math.min(3, Number(m[1]))}` */ });
  throw err;
}

Prevention

When it happens

Trigger: Passing `symbol=myFunc#3` when the target line contains fewer than 3 occurrences of `myFunc` — execute() → resolveSymbolColumn → parseSymbolSpec gives occurrence=3 but fallbackIndexes.length < 3.

Common situations: Guessing occurrence numbers in agent prompts; the line was edited so a repeated identifier count changed; copying a `#N` spec from a different line.

Related errors


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