laurent22/joplin · error · Error
Ambiguous notebook "%s". Please use short notebook id instea
Error message
Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id
What it means
Thrown by `mv <item> <notebook>` when the destination was found by loadItem but `Folder.search({ titlePattern: destination, limit: 2 })` returns >1 row. Same LIKE-matching ambiguity as mkbook's validDestinationFolder: the destination string is a substring of multiple notebook titles, so the move target is ambiguous even though an exact match exists.
Source
Thrown at packages/app-cli/app/command-mv.ts:29
}
public override description() {
return _('Moves the given <item> to [notebook]');
}
public override async action(args: { item: string; 'notebook': string }) {
const pattern = args['item'];
const destination = args['notebook'];
let folder = null;
if (destination !== 'root') {
folder = await app().loadItem(ModelType.Folder, destination);
if (!folder) throw new Error(_('Cannot find "%s".', destination));
}
const destinationDuplicates = await Folder.search({ titlePattern: destination, limit: 2 });
if (destinationDuplicates.length > 1) {
throw new Error(_('Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id', destination));
}
const itemFolder = await app().loadItem(ModelType.Folder, pattern);
if (itemFolder) {
const sourceDuplicates = await Folder.search({ titlePattern: pattern, limit: 2 });
if (sourceDuplicates.length > 1) {
throw new Error(_('Ambiguous notebook "%s". Please use notebook id instead - press "ti" to see the short notebook id or use $b for current selected notebook', pattern));
}
if (destination === 'root') {
await Folder.moveToFolder(itemFolder.id, '');
} else {
await Folder.moveToFolder(itemFolder.id, folder.id);
}
} else {
const notes = await app().loadItems(ModelType.Note, pattern);
if (notes.length === 0) throw new Error(_('Cannot find "%s".', pattern));
for (let i = 0; i < notes.length; i++) {
await Note.moveToFolder(notes[i].id, folder.id);View on GitHub (pinned to 2654b33620)
Solutions
- Use the short notebook id: `joplin mv <item> <short-id>`
- Rename colliding notebooks to disambiguate
- Use the full 32-char folder id
Example fix
// before joplin mv mynote Note // Note LIKE-matches "Notes" + "My Notes" -> throws // after joplin mv mynote 7f3a // short id
Defensive patterns
Strategy: validation
Validate before calling
import Folder from '@joplin/lib/models/Folder';
async function assertDestinationUnambiguous(destRef: string) {
const dups = await Folder.search({ titlePattern: destRef, limit: 2 });
if (dups.length > 1) {
throw new Error(`Destination "${destRef}" is ambiguous. Use the short notebook id.`);
}
} Type guard
const isUnambiguous = async (ref: string): Promise<boolean> =>
(await Folder.search({ titlePattern: ref, limit: 2 })).length <= 1; Prevention
- Use short notebook ids for destinations whenever titles overlap
- Rename colliding notebooks to disambiguate
- Avoid overly short destination names in large catalogs
When it happens
Trigger: Destination name LIKE-matches several notebooks (e.g., `mv Note X` when "Notes" and "My Notes" both exist); short destination strings; overlapping names in a large catalog.
Common situations: Notebook tree with similarly-named folders; abbreviated destination references in batch scripts.
Related errors
- Ambiguous notebook "%s". Please use notebook id instead - pr
- Ambiguous notebook "%s". Please use short notebook id instea
- Cannot find "%s".
- Cannot find "%s".
- Cannot find: "%s"
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/727c0b633fcfb1df.
Report an issue: GitHub.