laurent22/joplin · error · Error

Cannot find "%s".

Error message

Cannot find "%s".

What it means

Thrown by `mv <item> <notebook>` when the destination is not the literal string 'root' and `app().loadItem(ModelType.Folder, destination)` returns null. 'root' is special-cased just above and bypasses the lookup (it moves the item to the top level). Folder lookup is global by exact title, full id, or partial id >=2 chars.

Source

Thrown at packages/app-cli/app/command-mv.ts:24

import Note from '@joplin/lib/models/Note';

class Command extends BaseCommand {
	public override usage() {
		return 'mv <item> [notebook]';
	}

	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);
			}

View on GitHub (pinned to 2654b33620)

Solutions

  1. List notebooks with `joplin ls /` and use the exact title or id
  2. Use the literal `root` as the destination to move to the top level
  3. Use the short notebook id (toggle with `ti`)

Example fix

// before
joplin mv ProjectA Projcts   // typo, throws

// after
joplin ls /
# title is "Projects"
joplin mv ProjectA Projects
# or move to top level:
joplin mv ProjectA root
Defensive patterns

Strategy: validation

Validate before calling

import app from './app';
import { ModelType } from '@joplin/lib/BaseModel';

async function resolveMoveDestination(destRef: string) {
  if (destRef === 'root') return null; // top-level move
  const folder = await app().loadItem(ModelType.Folder, destRef);
  if (!folder) throw new Error(`Cannot find destination notebook "${destRef}". Verify with \`ls /\` or use \`root\`.`);
  return folder;
}

Type guard

const isFolderOrNullRoot = (f: any, ref: string): boolean =>
  ref === 'root' ? true : (!!f && typeof f.id === 'string');

Prevention

When it happens

Trigger: Typo in the destination notebook name; destination deleted/renamed; partial id <2 chars; forgetting the literal `root` keyword when moving to the top level.

Common situations: Restructuring notebooks and misspelling the target; using a note id where a notebook id is required.

Related errors


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