laurent22/joplin · error · Error

Cannot find: "%s"

Error message

Cannot find: "%s"

What it means

Thrown by `mkbook -p <parent> <new>` via `validDestinationFolder` when `app().loadItem(ModelType.Folder, targetFolder)` returns null. Note the message uses a colon ("Cannot find: \"%s\"") unlike the rest of the codebase. The method is also used by other notebook-targeting flows that need to validate a parent.

Source

Thrown at packages/app-cli/app/command-mkbook.ts:28

		return 'mkbook <new-notebook>';
	}

	public description() {
		return _('Creates a new notebook.');
	}

	public options() {
		return [
			['-p, --parent <parent-notebook>', _('Create a new notebook under a parent notebook.')],
		];
	}

	// validDestinationFolder check for presents and ambiguous folders
	public async validDestinationFolder(targetFolder: string) {

		const destinationFolder = await app().loadItem(ModelType.Folder, targetFolder);
		if (!destinationFolder) {
			throw new Error(_('Cannot find: "%s"', targetFolder));
		}

		const destinationDups = await Folder.search({ titlePattern: targetFolder, limit: 2 });
		if (destinationDups.length > 1) {
			throw new Error(_('Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id', targetFolder));
		}

		return destinationFolder;
	}

	public async saveAndSwitchFolder(newFolder: FolderEntity) {

		const folder = await Folder.save(newFolder, { userSideValidation: true });
		app().switchCurrentFolder(folder);

	}

	public async action(args: { 'new-notebook': string; options: { parent?: string } }) {

View on GitHub (pinned to 2654b33620)

Solutions

  1. List notebooks with `joplin ls /` and copy the exact parent title or id
  2. Use the short notebook id (toggle ids with `ti`)
  3. Drop `-p` to create a top-level notebook instead

Example fix

// before
joplin mkbook -p Work Projcts "Q3 Report"   // typo, throws

// after
joplin ls /
# parent title is "Projects"
joplin mkbook -p Projects "Q3 Report"
# or by short id:
joplin mkbook -p 7f3a "Q3 Report"
Defensive patterns

Strategy: validation

Validate before calling

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

async function resolveParentFor(parentRef: string) {
  const folder = await app().loadItem(ModelType.Folder, parentRef);
  if (!folder) throw new Error(`Cannot find parent notebook "${parentRef}". Verify with \`ls /\`.`);
  return folder;
}

Type guard

const isFolderRef = (f: any): f is { id: string; title: string } =>
  !!f && typeof f.id === 'string';

Prevention

When it happens

Trigger: `mkbook -p NonExistent NewBook`; typo in parent name; parent was deleted; partial id <2 chars.

Common situations: Creating a sub-notebook under a parent whose title was mistyped; parent notebook renamed between script write and run.

Related errors


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