jackwener/OpenCLI · error · CommandExecutionError

NotebookLM ${action} succeeded but the notebook ${notebookId

Error message

NotebookLM ${action} succeeded but the notebook ${notebookId} was not found in the post-write verification

What it means

verifyNotebooklmNotebookExists performs a post-write check: after a mutating action it fetches the notebook detail via RPC and confirms detail.id matches the requested notebookId. If the detail is null or the id differs, it throws this CommandExecutionError, meaning the write reported success but the notebook cannot be confirmed to exist. The library treats unverified writes as failures so callers don't assume durable state.

Source

Thrown at clis/notebooklm/utils.js:476

    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_LIST_RPC_ID, [null, 1, null, [2]]);
    return parseNotebooklmListResult(rpc.result);
}
export async function getNotebooklmDetailViaRpc(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return null;
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID, [state.notebookId, null, [2], null, 0]);
    return parseNotebooklmNotebookDetailResult(rpc.result);
}
export async function getNotebooklmNotebookDetailById(page, notebookId) {
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID, [notebookId, null, [2], null, 0]);
    return { detail: parseNotebooklmNotebookDetailResult(rpc.result), sources: parseNotebooklmSourceListResult(rpc.result) };
}
export async function verifyNotebooklmNotebookExists(page, notebookId, action) {
    try {
        const { detail } = await getNotebooklmNotebookDetailById(page, notebookId);
        if (!detail || detail.id !== notebookId) {
            throw new CommandExecutionError(`NotebookLM ${action} succeeded but the notebook ${notebookId} was not found in the post-write verification`);
        }
        return detail;
    }
    catch (error) {
        if (error instanceof AuthRequiredError || error instanceof CommandExecutionError)
            throw error;
        throw new CommandExecutionError(`NotebookLM ${action} post-write verification failed: ${error?.message || error}`);
    }
}
export async function verifyNotebooklmSourceAdded(page, notebookId, sourceId, action) {
    try {
        const { detail, sources } = await getNotebooklmNotebookDetailById(page, notebookId);
        if (!detail || detail.id !== notebookId) {
            throw new CommandExecutionError(`NotebookLM ${action} succeeded but the notebook ${notebookId} was not found in the post-write verification`);
        }
        const matched = sources.find((s) => s.id === sourceId);
        if (!matched) {
            throw new CommandExecutionError(`NotebookLM ${action} succeeded but source ${sourceId} did not appear in the notebook's source list`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the verification after a short delay to rule out propagation lag
  2. Reload the NotebookLM page and re-fetch the notebook list to confirm the notebook's actual state
  3. Check that the browser session is authenticated and not sitting on a login redirect (AuthRequiredError path)
  4. Verify the notebookId passed in matches the id returned by the create/list call
  5. Re-run the original action if the notebook genuinely wasn't persisted

Example fix

// before: assuming write success
await createNotebook(...);
// after: verify with retry/backoff
let detail;
for (let i = 0; i < 3; i++) {
  try { detail = await verifyNotebooklmNotebookExists(page, id, 'create'); break; }
  catch (e) { if (i === 2) throw e; await page.wait(2); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the id exists in the list before relying on it
const notebooks = await listNotebooklmViaRpc(page);
if (!notebooks.some((n) => n.id === notebookId)) {
  throw new Error(`Notebook ${notebookId} not present in list; skipping write verification`);
}

Type guard

function isVerifiedDetail(detail, notebookId) {
  return Boolean(detail) && typeof detail.id === 'string' && detail.id === notebookId;
}

Try / catch

try {
  await verifyNotebooklmNotebookExists(page, notebookId, 'create');
} catch (e) {
  if (e instanceof AuthRequiredError) return reauthenticate();
  // treat as not-verified: re-list and retry once after backoff
  await page.wait(3);
  const found = (await listNotebooklmViaRpc(page)).some((n) => n.id === notebookId);
  if (!found) throw e;
}

Prevention

When it happens

Trigger: Calling verifyNotebooklmNotebookExists (after create/rename/delete-style actions) when getNotebooklmNotebookDetailById returns null detail, or a detail whose id !== notebookId — e.g. the RPC detail response is an empty/unwrapped-missing payload or a different notebook was returned.

Common situations: NotebookLM replication lag after create/delete; the notebook was deleted by another session/tab; the browser session was redirected (login wall) so the detail RPC returned an unexpected payload; RPC schema change makes parseNotebooklmNotebookDetailResult return null.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/bb86f0cc82e3b90c. Report an issue: GitHub.