{"record":{"id":"b68a3f33b200c384","repo":"jackwener/OpenCLI","slug":"invalid-source-url-url","errorCode":null,"errorMessage":"Invalid source URL: \"${url}\"","messagePattern":"Invalid source URL: \"(.+?)\"","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/add-source.js","lineNumber":73,"sourceCode":"}\n\nexport function buildRegisterFileSourceArgs(projectId, filename) {\n    return [\n        [[filename]],\n        projectId,\n        [2],\n        [1, null, null, null, null, null, null, null, null, null, [1]],\n    ];\n}\n\nexport function parseSourceUrl(value) {\n    const url = String(value ?? '').trim();\n    if (!url) return '';\n    let parsed;\n    try {\n        parsed = new URL(url);\n    } catch {\n        throw new ArgumentError(`Invalid source URL: \"${url}\"`, 'URL must be a valid http:// or https:// URL.');\n    }\n    if ((parsed.protocol !== 'http:' && parsed.protocol !== 'https:') || !parsed.hostname) {\n        throw new ArgumentError(`Invalid source URL: \"${url}\"`, 'URL must start with http:// or https://.');\n    }\n    return parsed.toString();\n}\n\nexport function parseSourceText(value) {\n    if (value === undefined || value === null) return null;\n    const text = String(value);\n    if (!text.trim()) throw new ArgumentError('--content must not be empty');\n    if (text.length > MAX_TEXT_SOURCE_BYTES) {\n        throw new ArgumentError(`--content exceeds ${MAX_TEXT_SOURCE_BYTES} bytes; split into smaller sources or upload as a file.`);\n    }\n    return text;\n}\n\nexport function parseSourceTitle(value, fallback) {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/add-source.js#L55-L91","documentation":"parseSourceUrl throws this ArgumentError when the --url value cannot be parsed by the WHATWG URL constructor (new URL throws), e.g. a missing scheme, spaces, or malformed host. The second detail argument tells the user the URL must be a valid http:// or https:// URL. It is the first of two URL validations before the value is accepted as a NotebookLM source URL.","triggerScenarios":"Passing --url 'example.com/page' (no scheme), '--url htp://...' (typo'd scheme), a URL with unencoded spaces, or an empty-but-truthy string that fails URL parsing.","commonSituations":"Copy-pasting a URL that lost its https:// prefix; smart-quote characters from docs breaking the parse; shell stripping the scheme; forgetting to quote URLs containing & or ? in some shells.","solutions":["Add the scheme: prefix the value with https:// if it was omitted.","Fix typos in the scheme/host and remove stray whitespace or smart quotes.","Quote the URL in your shell so &, ?, and spaces survive intact.","Test with `new URL(value)` in Node to confirm it parses before running the command."],"exampleFix":"// before\naddSource({ url: 'example.com/article' });\n// after\naddSource({ url: 'https://example.com/article' });","handlingStrategy":"validation","validationCode":"function isParseableUrl(v) {\n  try { new URL(String(v ?? '').trim()); return true; } catch { return false; }\n}","typeGuard":"function isValidHttpUrl(v) {\n  try { const u = new URL(String(v ?? '').trim()); return (u.protocol === 'http:' || u.protocol === 'https:') && !!u.hostname; } catch { return false; }\n}","tryCatchPattern":"try {\n  await addSource({ url });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.startsWith('Invalid source URL')) {\n    console.error(`Add https:// and re-check the URL: \"${url}\"`);\n  } else throw e;\n}","preventionTips":["Always include the https:// scheme in stored URLs.","Trim and validate URLs with new URL() before passing them.","Quote URLs in shells so & and ? aren't interpreted.","Strip smart quotes/whitespace when copying URLs from docs or chat."],"tags":["url","argument-validation","notebooklm","parsing"],"backgroundTag":"invalid-url","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}