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

  1. Use the notebook id of the source: `joplin mv <short-id> <destination>`
  2. Use `$b` for the currently selected notebook: `joplin mv $b <destination>` (works in interactive mode)
  3. 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

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


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/4c980dcfc3a9149c. Report an issue: GitHub.