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
- Re-run the verification after a short delay to rule out propagation lag
- Reload the NotebookLM page and re-fetch the notebook list to confirm the notebook's actual state
- Check that the browser session is authenticated and not sitting on a login redirect (AuthRequiredError path)
- Verify the notebookId passed in matches the id returned by the create/list call
- 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
- Always verify writes instead of trusting the action's success return
- Allow a short delay after create/delete before verification (replication lag)
- Keep the browser session authenticated to avoid AuthRequiredError masking
- Cross-check the notebook id against the list RPC before acting
- Handle AuthRequiredError separately from verification failures
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
- NotebookLM ${action} post-write verification failed: ${error
- NotebookLM ${action} succeeded but source ${sourceId} did no
- NOTEBOOKLM_TOKENS
- Not a git repository
- Working tree not clean: ${status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/bb86f0cc82e3b90c.
Report an issue: GitHub.