jackwener/OpenCLI · error · CommandExecutionError

NotebookLM ${action} post-write verification failed: ${error

Error message

NotebookLM ${action} post-write verification failed: ${error?.message || error}

What it means

Inside verifyNotebooklmNotebookExists, any error thrown while fetching or parsing the notebook detail that is not an AuthRequiredError or CommandExecutionError is wrapped into this CommandExecutionError with the original message appended. It means the post-write verification itself failed (network hiccup, malformed RPC payload, browser bridge failure), not necessarily that the notebook is missing.

Source

Thrown at clis/notebooklm/utils.js:483

    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`);
        }
        return matched;
    }
    catch (error) {
        if (error instanceof AuthRequiredError || error instanceof CommandExecutionError)
            throw error;
        throw new CommandExecutionError(`NotebookLM ${action} post-write verification failed: ${error?.message || error}`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the inner error message appended after 'post-write verification failed:' to find the root cause
  2. Retry the verification — transient transport failures often succeed on a second attempt
  3. Reload the NotebookLM notebook page to re-establish page state before re-verifying
  4. Re-authenticate if the session expired (check for login redirects)
  5. Upgrade the library if NotebookLM changed its internal RPC schema

Example fix

// before: single-shot verify
await verifyNotebooklmNotebookExists(page, id, 'create');
// after: tolerate transient verification failures
try {
  await verifyNotebooklmNotebookExists(page, id, 'create');
} catch (e) {
  if (!/verification failed/.test(e.message)) throw e;
  await page.wait(3);
  await verifyNotebooklmNotebookExists(page, id, 'create');
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure page is on a NotebookLM notebook URL before verification
const url = page.url();
if (!/notebooklm\.google\.com\/.+/.test(url)) {
  throw new Error('Page not on NotebookLM; verification would fail');
}

Type guard

function isTransientVerificationError(e) {
  return /post-write verification failed/.test(String(e?.message));
}

Try / catch

try {
  await verifyNotebooklmNotebookExists(page, id, action);
} catch (e) {
  if (e instanceof AuthRequiredError || !isTransientVerificationError(e)) throw e;
  await page.wait(2);
  await verifyNotebooklmNotebookExists(page, id, action); // single retry
}

Prevention

When it happens

Trigger: getNotebooklmNotebookDetailById throws during verification — e.g. evaluateNotebooklm/RPC transport failure, parseNotebooklmNotebookDetailResult hitting a schema change (null detail path throws differently), timeouts waiting for the page, or a non-auth browser error while the verify wrapper converts it.

Common situations: Flaky network or page navigation during verification; Browser Bridge disconnect mid-RPC; NotebookLM RPC schema change returning unexpected payloads; slow page load making the detail probe fail before data arrives.

Related errors


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