can1357/oh-my-pi · error · ToolError
multiple windows match ${JSON.stringify(selector)}:\n${candi
Error message
multiple windows match ${JSON.stringify(selector)}:\n${candidates} What it means
Thrown by the computer tool's desktop.window(selector) API when the selector matches more than one open window. Because a Win handle must refer to exactly one window, the tool refuses to guess and lists every matching candidate (id, app, title) in the message so the caller can disambiguate.
Source
Thrown at packages/coding-agent/src/tools/computer/worker.ts:685
windows: async (filter?: WindowFilter): Promise<DesktopWindow[]> => {
const { signal } = getContext();
return (await nativeCall(signal, () => session.listWindows())).filter(window =>
matchesFilter(window, filter),
);
},
window: async (selector: string | WindowFilter): Promise<Win> => {
const { signal } = getContext();
const windows = await nativeCall(signal, () => session.listWindows());
const matches =
typeof selector === "string"
? windows.filter(window => window.id === selector)
: windows.filter(window => matchesFilter(window, selector));
if (matches.length === 0) throw new ToolError(`no window matches ${JSON.stringify(selector)}`);
if (matches.length > 1) {
const candidates = matches
.map(window => `${window.id} ${window.app} ${JSON.stringify(window.title)}`)
.join("\n");
throw new ToolError(`multiple windows match ${JSON.stringify(selector)}:\n${candidates}`);
}
return makeWin(matches[0]!);
},
focusedWindow: async (): Promise<Win | null> => {
const { signal } = getContext();
const window = (await nativeCall(signal, () => session.listWindows())).find(candidate => candidate.focused);
return window ? makeWin(window) : null;
},
screenshot: (options?: ScreenshotOptions) => captureScreenshot(session, getContext, "desktop", options),
click: desktopTarget.click.bind(desktopTarget),
doubleClick: desktopTarget.doubleClick.bind(desktopTarget),
move: desktopTarget.move.bind(desktopTarget),
drag: desktopTarget.drag.bind(desktopTarget),
scroll: desktopTarget.scroll.bind(desktopTarget),
type: desktopTarget.type.bind(desktopTarget),
press: desktopTarget.press.bind(desktopTarget),
elementAt: async (x: number, y: number): Promise<El | null> => {
const { signal } = getContext();View on GitHub (pinned to 9690622007)
Solutions
- Narrow the selector: add the exact window title or use the specific window id from the candidate list printed in the error.
- Call desktop.windows(filter) to enumerate matches and pick one programmatically (e.g. the focused one or by title substring).
- Add more filter criteria (app plus title) until exactly one window matches.
- If operating on any matching window is fine, take matches[0] from windows() instead of using window().
Example fix
// before
const win = await desktop.window({app: 'Chrome'});
// after
const chromeWins = await desktop.windows({app: 'Chrome'});
const win = await desktop.window(chromeWins.find(w => w.title.includes('Docs'))!.id); Defensive patterns
Strategy: validation
Validate before calling
const matches = await desktop.windows(selector);
if (matches.length > 1) throw new Error(`ambiguous: ${matches.length} windows match`);
if (matches.length === 0) throw new Error('no match');
const win = await desktop.window(matches[0].id); Try / catch
try {
const win = await desktop.window(selector);
} catch (err) {
if (String(err?.message).startsWith('multiple windows match')) {
const matches = await desktop.windows(selector);
const win = await desktop.window(matches[0].id); // or pick by title
} else throw err;
} Prevention
- Filter by unique window id, not app or partial title.
- Combine app + exact title filters to guarantee uniqueness.
- Enumerate with windows() first and assert length === 1.
- Avoid selectors that are valid on single-window dev machines but ambiguous in real sessions.
When it happens
Trigger: Calling desktop.window({app: 'Chrome'}) with multiple Chrome windows open; calling desktop.window({title: 'Untitled'}) matching several documents; any filter (or id-less selection) that is not unique across the current window list.
Common situations: Browsers and editors commonly have many windows with similar titles; automation written against a single-window machine breaks on a multi-window desktop; partial title filters that were unique during development match several windows in production.
Related errors
- no window matches ${JSON.stringify(selector)}
- Found ${occurrences} occurrences${pathSuffix}${moreMsg}:\n\n
- Found ${result.matchCount} matches for context '${displayCon
- Found ${searchResult.matchCount} matches for the text in ${p
- Operation ${operationNumber} is ambiguous: ${candidates.leng
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e1e7d25550b05ab1.
Report an issue: GitHub.