{"record":{"id":"e7c57d2ac8fb5705","repo":"jackwener/OpenCLI","slug":"notebooklm-file-upload-failed","errorCode":null,"errorMessage":"NotebookLM file upload failed: ","messagePattern":"NotebookLM file upload failed: ","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/add-source.js","lineNumber":182,"sourceCode":"          'X-Goog-Upload-Offset': '0',\n          'X-Goog-AuthUser': ${JSON.stringify(authuser)},\n        },\n        body: bytes,\n      });\n      if (!uploadRes.ok) {\n        const status = uploadRes.headers.get('X-Goog-Upload-Status') || '';\n        const text = (await uploadRes.text()).slice(0, 400);\n        return { error: 'upload failed HTTP ' + uploadRes.status + (status ? ' upload-status=' + status : '') + (text ? ' body=' + text : '') };\n      }\n      return { ok: true, status: uploadRes.status };\n    } catch (e) {\n      return { error: 'upload exception: ' + ((e && e.message) || String(e)) };\n    }\n  })()`;\n    const raw = await page.evaluate(script);\n    const result = raw && typeof raw === 'object' && 'data' in raw && 'session' in raw ? raw.data : raw;\n    if (!result || result.error) {\n        throw new CommandExecutionError('NotebookLM file upload failed: ' + (result?.error || 'unknown error'));\n    }\n    return result;\n}\n\ncli({\n    site: NOTEBOOKLM_SITE,\n    name: 'add-source',\n    access: 'write',\n    description: 'Add a URL, text, or local file source to an existing NotebookLM notebook',\n    domain: NOTEBOOKLM_DOMAIN,\n    strategy: Strategy.COOKIE,\n    browser: true,\n    navigateBefore: false,\n    args: [\n        { name: 'notebook', positional: true, required: true, help: 'Notebook id from `notebooklm list` or full notebook URL' },\n        { name: 'url', help: 'Source URL to add (http/https). Pass exactly one of --url, --content, --file.' },\n        { name: 'content', help: 'Raw text content to add as a Text source (max 10 MB).' },\n        { name: 'file', help: `Local file path to upload as a source (max ${MAX_FILE_SOURCE_BYTES} bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol.` },","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/add-source.js#L164-L200","documentation":"uploadFileViaDriveResumable throws this CommandExecutionError when the in-page Drive resumable upload script returns no result or carries an error field; the message appends result.error or the literal 'unknown error'. This is the point where a NotebookLM file upload executed inside the browser page (via page.evaluate) failed — network, auth, quota, or a JS exception inside the page script.","triggerScenarios":"The page.evaluate upload script throws (caught and returned as 'upload exception: ...'), the Drive resumable session returns a non-2xx, the browser session is logged out, or the result object is missing/null so the fallback 'unknown error' is used.","commonSituations":"Expired NotebookLM/Google session cookies mid-automation; Drive API quota or CORS/network failures inside the page; oversized files timing out during the resumable upload; site DOM/script changes breaking the injected evaluate script.","solutions":["Read the appended detail after 'NotebookLM file upload failed: ' to identify the concrete cause (exception text vs Drive error).","Re-authenticate / refresh the browser session if the error indicates auth or a 401/403.","Retry the upload for transient network errors; consider chunked resumable retries.","Verify the file passes the size checks (see readFileForUpload) and that the site script hasn't changed; update the automation if Drive endpoints moved."],"exampleFix":"// before\nconst raw = await page.evaluate(script); // throws vague failure\n// after\nconst raw = await page.evaluate(script);\nif (!raw) throw new CommandExecutionError('upload returned no result — check session/login state');\nif (raw.error?.includes('401') || raw.error?.includes('auth')) await relogin();","handlingStrategy":"try-catch","validationCode":"// before uploading: confirm session and file constraints\nconst stat = fs.statSync(filePath);\nif (!stat.isFile() || stat.size > MAX_FILE_SOURCE_BYTES) throw new Error('File unsuitable for upload');\n// ensure the browser session is logged in before invoking\nawait ensureNotebooklmSession(page);","typeGuard":"function isUploadResult(r) { return r && typeof r === 'object' && !('error' in r && r.error); }","tryCatchPattern":"try {\n  await addSource({ file: filePath });\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.startsWith('NotebookLM file upload failed')) {\n    const detail = e.message.slice('NotebookLM file upload failed: '.length);\n    if (/401|403|auth/i.test(detail)) await relogin(page);\n    else if (/network|timeout/i.test(detail)) await retryWithBackoff(() => addSource({ file: filePath }));\n    else console.error('Upload failed:', detail);\n  } else throw e;\n}","preventionTips":["Refresh the Google/NotebookLM session before long automation runs.","Pre-validate file size and type before attempting the upload.","Add retry with backoff for transient Drive/network errors.","Log the detail appended after the error prefix to diagnose quickly.","Update the injected page script when Drive/NotebookLM endpoints or DOM change."],"tags":["notebooklm","file-upload","browser-automation","google-drive"],"backgroundTag":"upload-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}