laurent22/joplin · error · Error
Ambiguous notebook "%s". Please use notebook id instead - pr
Error message
Ambiguous notebook "%s". Please use notebook id instead - press "ti" to see the short notebook id or use $b for current selected notebook
What it means
Thrown by `mv <item> <notebook>` when the *source* pattern resolved to a folder (`itemFolder` truthy) but `Folder.search({ titlePattern: pattern, limit: 2 })` returns >1 row. Message differs from the destination-ambiguous message: it suggests `$b` for the currently selected notebook, because the source is the item being moved (where the current notebook shortcut is more relevant).
Source
Thrown at packages/app-cli/app/command-mv.ts:36
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);
}
}
}
}
module.exports = Command;
View on GitHub (pinned to 2654b33620)
Solutions
- Use the notebook id of the source: `joplin mv <short-id> <destination>`
- Use `$b` for the currently selected notebook: `joplin mv $b <destination>` (works in interactive mode)
- Rename the source notebook to a unique title first
Example fix
// before joplin mv Note Projects // "Note" LIKE-matches "Notes" and "Notebook" -> throws // after joplin mv 7f3a Projects # by short id # or in interactive mode, with the source notebook selected: :mv $b Projects
Defensive patterns
Strategy: validation
Validate before calling
import Folder from '@joplin/lib/models/Folder';
async function assertSourceNotebookUnambiguous(pattern: string) {
const dups = await Folder.search({ titlePattern: pattern, limit: 2 });
if (dups.length > 1) {
throw new Error(`Source notebook "${pattern}" is ambiguous. Use its id, or $b for the current notebook.`);
}
} Type guard
const isUnambiguousSource = async (ref: string): Promise<boolean> =>
(await Folder.search({ titlePattern: ref, limit: 2 })).length <= 1; Prevention
- Use `$b` in interactive mode to refer to the currently selected notebook as the source
- Pass notebook ids when scripting moves
- Rename ambiguous source notebooks before bulk reorganization
When it happens
Trigger: Moving a notebook whose name LIKE-matches other notebooks; source pattern is a short substring common to multiple folders.
Common situations: Reorganizing a flat tree with many similarly-named notebooks; scripting moves by partial names.
Related errors
- Ambiguous notebook "%s". Please use short notebook id instea
- 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/4c980dcfc3a9149c.
Report an issue: GitHub.