{"record":{"id":"bbc26fa5a5575ce1","repo":"jackwener/OpenCLI","slug":"pass-exactly-one-of-url-content-file-got","errorCode":null,"errorMessage":"Pass exactly one of --url, --content, --file (got: ","messagePattern":"Pass exactly one of --url, --content, --file \\(got: ","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/notebooklm/add-source.js","lineNumber":216,"sourceCode":"        { 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.` },\n        { name: 'title', help: 'Title for the text source (default \"Text Source\"). Ignored for --url and --file.' },\n        { name: 'mime-type', help: 'Override the auto-detected MIME type when --file is given.' },\n        { name: 'execute', type: 'boolean', help: 'Actually add the remote source to the NotebookLM notebook' },\n    ],\n    columns: ['notebook_id', 'source_id', 'kind', 'identifier', 'notebook_url'],\n    func: async (page, kwargs) => {\n        const notebookId = parseNotebooklmNotebookTarget(String(kwargs.notebook ?? ''));\n        const url = parseSourceUrl(kwargs.url);\n        const content = parseSourceText(kwargs.content);\n        const filePath = typeof kwargs.file === 'string' && kwargs.file.trim() ? kwargs.file.trim() : '';\n        const modes = [url ? 'url' : '', content !== null ? 'text' : '', filePath ? 'file' : ''].filter(Boolean);\n        if (modes.length === 0) {\n            throw new ArgumentError('Pass exactly one of --url <url>, --content <text>, or --file <path>');\n        }\n        if (modes.length > 1) {\n            throw new ArgumentError('Pass exactly one of --url, --content, --file (got: ' + modes.join(' + ') + ')');\n        }\n        requireNotebooklmExecute(kwargs.execute, 'add a NotebookLM source');\n        const title = parseSourceTitle(kwargs.title, 'Text Source');\n        await ensureNotebooklmHome(page);\n        await requireNotebooklmSession(page);\n        if (filePath) {\n            const file = readFileForUpload(filePath);\n            const mime = inferMimeType(file.filename, kwargs['mime-type']);\n            const registerRpc = await callNotebooklmRpc(page, NOTEBOOKLM_ADD_FILE_SOURCE_RPC_ID, buildRegisterFileSourceArgs(notebookId, file.filename));\n            const sourceId = parseAddSourceResult(registerRpc.result, [notebookId]);\n            if (!sourceId) {\n                throw new CommandExecutionError('NotebookLM AddFileSource (o4cbdc) RPC returned no source id; cannot start file upload.');\n            }\n            await uploadFileViaDriveResumable(page, notebookId, sourceId, file.filename, file.base64, file.size);\n            await verifyNotebooklmSourceAdded(page, notebookId, sourceId, 'add-source --file');\n            return [{\n                notebook_id: notebookId,\n                source_id: sourceId,","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/notebooklm/add-source.js#L198-L234","documentation":"The notebooklm add-source command requires exactly one input mode: --url, --content, or --file. This ArgumentError is thrown when the argument parser resolves more than one mode simultaneously, listing the detected modes (e.g. 'text + file') in the message so you can see which flags collided.","triggerScenarios":"Running `add-source` with two or more of --url, --content, --file set to non-empty values, e.g. `add-source --url https://x --content foo`. Modes are derived: url truthy, content !== null, and file a non-empty trimmed string.","commonSituations":"Shell scripts passing a default --file plus a user-supplied --content; wrapping code that always sets content (e.g. content='' is treated as set since only null counts as absent); copy-pasting an example command that had two flags.","solutions":["Remove all but one of --url, --content, --file from the command line.","If wrapping in a script, conditionally include flags: only pass --content when the user actually provided text.","Empty strings for --url/--file are ignored, but any non-null --content counts as a mode — pass null/omit it entirely when unused."],"exampleFix":"// before\naddSource({ url: 'https://example.com', content: 'paste' })\n// after\naddSource({ url: 'https://example.com' })","handlingStrategy":"validation","validationCode":"const modes = [argv.url && 'url', argv.content != null && 'content', argv.file && 'file'].filter(Boolean);\nif (modes.length !== 1) throw new Error(`Pass exactly one of --url, --content, --file (got: ${modes.join(' + ') || 'none'})`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build commands programmatically so only one input flag is ever set.","Treat --content as set whenever it is non-null; pass null/omit when unused.","Add a pre-flight check in wrapper scripts before spawning the CLI."],"tags":["cli","argument-validation","notebooklm"],"backgroundTag":"mutually-exclusive-arguments","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}