{"record":{"id":"1f5a011e1d7cd357","repo":"laurent22/joplin","slug":"ambiguous-notebook-s-please-use-short-notebook","errorCode":null,"errorMessage":"Ambiguous notebook \"%s\". Please use short notebook id instead - press \"ti\" to see the short notebook id","messagePattern":"Ambiguous notebook \"(.+?)\"\\. Please use short notebook id instead - press \"ti\" to see the short notebook id","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-cli/app/command-mkbook.ts","lineNumber":33,"sourceCode":"\t}\n\n\tpublic options() {\n\t\treturn [\n\t\t\t['-p, --parent <parent-notebook>', _('Create a new notebook under a parent notebook.')],\n\t\t];\n\t}\n\n\t// validDestinationFolder check for presents and ambiguous folders\n\tpublic async validDestinationFolder(targetFolder: string) {\n\n\t\tconst destinationFolder = await app().loadItem(ModelType.Folder, targetFolder);\n\t\tif (!destinationFolder) {\n\t\t\tthrow new Error(_('Cannot find: \"%s\"', targetFolder));\n\t\t}\n\n\t\tconst destinationDups = await Folder.search({ titlePattern: targetFolder, limit: 2 });\n\t\tif (destinationDups.length > 1) {\n\t\t\tthrow new Error(_('Ambiguous notebook \"%s\". Please use short notebook id instead - press \"ti\" to see the short notebook id', targetFolder));\n\t\t}\n\n\t\treturn destinationFolder;\n\t}\n\n\tpublic async saveAndSwitchFolder(newFolder: FolderEntity) {\n\n\t\tconst folder = await Folder.save(newFolder, { userSideValidation: true });\n\t\tapp().switchCurrentFolder(folder);\n\n\t}\n\n\tpublic async action(args: { 'new-notebook': string; options: { parent?: string } }) {\n\t\tconst targetFolder = args.options.parent;\n\n\t\tconst newFolder: FolderEntity = {\n\t\t\ttitle: args['new-notebook'],\n\t\t};","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-cli/app/command-mkbook.ts#L15-L51","documentation":"Thrown by `validDestinationFolder` (used by `mkbook -p`) after a folder *was* found by exact title/id, but `Folder.search({ titlePattern: targetFolder, limit: 2 })` returns more than one row. search uses SQL LIKE matching, so the parent string also matches other notebooks whose titles contain it. loadItem (exact title or id) succeeded, but the LIKE search reveals the name is ambiguous across the catalog.","triggerScenarios":"Parent name is a substring of other notebook titles (e.g., `-p Note` when \"Notes\" and \"My Notes\" both exist); short parent strings that LIKE-match widely; duplicated exact titles across the tree (less common because loadItem itself would have thrown 'More than one item match' first).","commonSituations":"Notebook hierarchies with overlapping names; abbreviated parent references in scripts.","solutions":["Use the short notebook id: press `ti` in the GUI to reveal ids, then `mkbook -p <short-id> <new>`","Rename the colliding notebooks to disambiguate titles","Use the full 32-char folder id"],"exampleFix":"// before\njoplin mkbook -p Note SubBook   // Note LIKE-matches \"Notes\" and \"My Notes\" -> throws\n\n// after\njoplin mkbook -p 7f3a SubBook    // short id is unambiguous","handlingStrategy":"validation","validationCode":"import Folder from '@joplin/lib/models/Folder';\n\nasync function assertParentUnambiguous(parentRef: string) {\n  const dups = await Folder.search({ titlePattern: parentRef, limit: 2 });\n  if (dups.length > 1) {\n    throw new Error(`Parent \"${parentRef}\" is ambiguous (${dups.length}+ matches). Use the short notebook id.`);\n  }\n}","typeGuard":"const isUnambiguous = async (ref: string): Promise<boolean> =>\n  (await Folder.search({ titlePattern: ref, limit: 2 })).length <= 1;","tryCatchPattern":null,"preventionTips":["Use short notebook ids (toggle with `ti`) for `-p` whenever titles overlap","Avoid short parent names that are substrings of other notebook titles","Rename colliding notebooks to disambiguate the catalog"],"tags":["cli","mkbook","notebook-lookup","ambiguous","validation"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}