{"record":{"id":"2b378a054fcf7c65","repo":"laurent22/joplin","slug":"notebook-is-required","errorCode":null,"errorMessage":"[notebook] is required","messagePattern":"\\[notebook\\] is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-cli/app/command-share.ts","lineNumber":255,"sourceCode":"\t\tconst commandShareReject = (folderId: string) => (\n\t\t\tcommandShareAcceptOrReject(folderId, false)\n\t\t);\n\n\t\tconst commandShareDelete = async (folder: FolderEntity) => {\n\t\t\tconst force = args.options.force;\n\t\t\tconst ok = force ? true : await this.prompt(\n\t\t\t\t_('Unshare notebook \"%s\"? This may cause other users to lose access to the notebook.', folderTitle(folder)),\n\t\t\t\t{ booleanAnswerDefault: 'n' },\n\t\t\t);\n\t\t\tif (!ok) return;\n\n\t\t\tlogger.info('Unsharing folder', folder.id);\n\t\t\tawait ShareService.instance().unshareFolder(folder.id);\n\t\t\tawait reg.waitForSyncFinishedThenSync();\n\t\t};\n\n\t\tif (args.command === 'add' || args.command === 'remove' || args.command === 'delete') {\n\t\t\tif (!args.notebook) throw new Error('[notebook] is required');\n\t\t\tconst folder = await app().loadItemOrFail(ModelType.Folder, args.notebook);\n\n\t\t\tif (args.command === 'delete') {\n\t\t\t\treturn commandShareDelete(folder);\n\t\t\t} else {\n\t\t\t\tif (!args.user) throw new Error('[user] is required');\n\n\t\t\t\tconst email = args.user;\n\t\t\t\tif (args.command === 'add') {\n\t\t\t\t\treturn commandShareAdd(folder, email);\n\t\t\t\t} else if (args.command === 'remove') {\n\t\t\t\t\treturn commandShareRemove(folder, email);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (args.command === 'leave') {\n\t\t\tconst folder = args.notebook ? await app().loadItemOrFail(ModelType.Folder, args.notebook) : null;","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-cli/app/command-share.ts#L237-L273","documentation":"Thrown by the `share` command dispatcher when the subcommand is `add`, `remove`, or `delete` but no `[notebook]` argument was supplied. These three subcommands all require a target notebook to act on, so the argument is mandatory. The message echoes the CLI syntax token `[notebook]` so the user knows which slot is missing.","triggerScenarios":"Running `:share add`, `:share remove`, or `:share delete` without a following notebook argument. Because the command parses positionally, an empty or whitespace-only notebook value also trips this guard.","commonSituations":"Forgot the notebook argument; copy-paste truncated the command; scripting the CLI with an unset variable for the notebook.","solutions":["Re-run with the notebook title: `:share <subcommand> <notebook> [<user>]`.","Check the usage string `share <command> [notebook] [user]` via `:help share`.","When scripting, assert the notebook variable is non-empty before invoking the command."],"exampleFix":"// before\nif (!args.notebook) throw new Error('[notebook] is required');\n\n// after - point to usage\nif (!args.notebook) throw new Error('[notebook] is required. Usage: share <command> [notebook] [user]');","handlingStrategy":"validation","validationCode":"if (['add','remove','delete'].includes(command) && !args.notebook) {\n\tthrow new Error('[notebook] is required. Usage: share <command> [notebook] [user]');\n}","typeGuard":"const isSubcommandRequiringNotebook = (c: string): boolean => ['add','remove','delete'].includes(c);","tryCatchPattern":"try {\n\tawait app().executeCommand(['share', subcommand, notebook, user].filter(Boolean));\n} catch (e) {\n\tif (/\\[notebook\\] is required/.test(e.message)) { /* reprompt for notebook */ }\n\telse throw e;\n}","preventionTips":["When scripting, assert required positional args are non-empty before invoking.","Keep the usage string `share <command> [notebook] [user]` visible.","Validate the subcommand set early to give one clear error."],"tags":["share","argument-validation","notebook","cli"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}