jackwener/OpenCLI · error · CommandExecutionError
${before.reason || `Failed to inspect ${name} state.`}${deta
Error message
${before.reason || `Failed to inspect ${name} state.`}${detail} What it means
defineToggle's pin/unpin flow first reads the conversation's context-menu labels via readConversationMenuLabels; if that inspection fails (before.ok false), this CommandExecutionError is thrown combining before.reason and optional detail. The library cannot even determine the conversation's current pin state, so it aborts rather than clicking blindly.
Source
Thrown at clis/grok/pin.js:40
description: `${name === 'pin' ? 'Pin' : 'Unpin'} a Grok conversation by ID`,
domain: GROK_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
siteSession: 'persistent',
args: [
{ name: 'id', positional: true, type: 'string', required: true, help: 'Conversation UUID or grok.com/c/<uuid> URL' },
],
columns: ['status', 'id'],
func: async (page, kwargs) => {
const id = parseGrokSessionId(kwargs.id);
await ensureOnGrok(page);
if (!(await isLoggedIn(page))) throw authRequired();
const expectedState = name === 'pin' ? 'pinned' : 'unpinned';
const before = await readConversationMenuLabels(page, id);
if (!before.ok) {
const detail = before.detail ? ` ${before.detail}` : '';
throw new CommandExecutionError(`${before.reason || `Failed to inspect ${name} state.`}${detail}`, SESSION_HINT);
}
if (getPinStateFromMenuLabels(before.labels) === expectedState) {
return [{ status: `already-${expectedState}`, id }];
}
const result = await clickConversationMenuItem(page, id, accessLabels);
if (!result || !result.ok) {
const detail = result?.detail ? ` ${result.detail}` : '';
throw new CommandExecutionError(`${result?.reason || `Failed to ${name} conversation.`}${detail}`, SESSION_HINT);
}
const verified = await waitForConversationPinState(page, id, expectedState);
if (!verified.ok) {
const labels = verified.labels?.length ? ` labels=${JSON.stringify(verified.labels)}` : '';
throw new CommandExecutionError(
`${name} menu item was clicked, but the conversation did not verify as ${expectedState}.${labels}`,
SESSION_HINT,
);
}View on GitHub (pinned to 49907e53dc)
Solutions
- Verify the conversation id exists — open it in the grok.com tab.
- Confirm the browser tab is still logged into grok.com and retry.
- Reload the page to rebuild DOM state, then re-run the command.
- Update the library if menu selectors changed in a Grok UI update.
Example fix
// before cli pin --id 00000000-0000-4000-8000-000000000000 // deleted conversation // after cli pin --id <id-of-an-existing-conversation>
Defensive patterns
Strategy: validation
Validate before calling
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!UUID_RE.test(id)) throw new Error(`invalid conversation id: ${id}`);
// Also confirm the conversation exists before toggling:
const exists = await page.goto(`https://grok.com/c/${id}`, { waitUntil: 'domcontentloaded' });
if (!exists || page.url().includes('404')) throw new Error(`conversation ${id} not found`); Type guard
function isGrokConversationId(v) {
return typeof v === 'string' &&
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v.trim());
} Try / catch
try {
await cli.pin({ id });
} catch (e) {
if (e instanceof CommandExecutionError && e.message.startsWith('Failed to inspect')) {
console.error(`Cannot read pin state for ${id} — verify it exists and the tab is logged in.`);
} else throw e;
} Prevention
- Validate the conversation id (UUID v4 shape) before invoking pin/unpin.
- Confirm the conversation still exists — deleted chats fail inspection.
- Ensure the browser tab is logged in before toggle operations.
- Reload the tab periodically to keep DOM state fresh.
When it happens
Trigger: Calling the pin or unpin command with a conversation id whose menu cannot be opened/read — the conversation no longer exists, the menu DOM changed, or the page is in a logged-out state (auth is checked just before).
Common situations: Stale or mistyped conversation id for a deleted chat, Grok UI update renaming menu items, or expired session after the initial isLoggedIn check raced with a logout.
Related errors
- Grok composer rejected the prompt: ${JSON.stringify(sendResu
- Midjourney Imagine composer was not found.
- ChatGPT did not create a conversation URL after sending the
- Could not switch to ${wantModel} model
- Claude composer is not available on the current page.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1a9690a67cb57816.
Report an issue: GitHub.