laurent22/joplin · error · Error

Cannot find "%s".

Error message

Cannot find "%s".

What it means

Thrown by `import <path> [notebook]` when a notebook argument was supplied (`args.notebook` truthy) but `app().loadItem(ModelType.Folder, args.notebook)` returned null. The guard is `if (args.notebook && !destinationFolder)`, so omitting the notebook arg entirely falls through to `Folder.defaultFolder()` instead of throwing. Lookup is global (not parent-scoped) for folders.

Source

Thrown at packages/app-cli/app/command-import.ts:37

	public override options() {
		const service = InteropService.instance();
		const formats = service
			.modules()
			.filter(m => m.type === 'importer')
			.map(m => m.format);

		return [
			['--format <format>', _('Source format: %s', ['auto'].concat(unique(formats)).join(', '))],
			['-f, --force', _('Do not ask for confirmation.')],
			['--output-format <output-format>', _('Output format: %s', 'md, html')],
		];
	}

	public override async action(args: { path: string; 'notebook'?: string; options: { format?: string; outputFormat?: string } }) {
		let destinationFolder = await app().loadItem(ModelType.Folder, args.notebook);

		if (args.notebook && !destinationFolder) throw new Error(_('Cannot find "%s".', args.notebook));

		if (!destinationFolder) destinationFolder = await Folder.defaultFolder();

		const importOptions: ImportOptions = {};
		importOptions.path = args.path;
		importOptions.format = args.options.format ? args.options.format : 'auto';
		importOptions.destinationFolderId = destinationFolder ? destinationFolder.id : null;

		let lastProgress = '';

		// onProgress/onError supported by Enex import only

		importOptions.onProgress = progressState => {
			const line = [];
			line.push(_('Found: %d.', progressState.loaded));
			line.push(_('Created: %d.', progressState.created));
			if (progressState.updated) line.push(_('Updated: %d.', progressState.updated));
			if (progressState.skipped) line.push(_('Skipped: %d.', progressState.skipped));

View on GitHub (pinned to 2654b33620)

Solutions

  1. Create the destination notebook first: `joplin mkbook <name>`, then re-run import with that name
  2. Omit the notebook argument entirely to import into the most recently created notebook (Folder.defaultFolder ordering)
  3. Verify the title with `joplin ls /` or use the short notebook id

Example fix

// before
joplin import notes.enex Incoming   // throws if "Incoming" doesn't exist

// after
joplin mkbook Incoming
joplin import notes.enex Incoming
// or omit notebook to use default:
joplin import notes.enex
Defensive patterns

Strategy: validation

Validate before calling

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

async function resolveImportDestination(notebookRef?: string) {
  if (!notebookRef) {
    const def = await Folder.defaultFolder();
    if (!def) throw new Error('No notebooks exist. Run `mkbook <name>` before importing.');
    return def;
  }
  const folder = await app().loadItem(ModelType.Folder, notebookRef);
  if (!folder) throw new Error(`Cannot find notebook "${notebookRef}". Create it with \`mkbook\` first.`);
  return folder;
}

Type guard

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

Prevention

When it happens

Trigger: Passing a notebook name that doesn't exist; typo; partial id <2 chars; expecting import to auto-create the destination notebook (it does not).

Common situations: First-time import workflow where the user assumes the destination notebook is created on demand; renamed target notebook; copy-paste error in scripts.

Related errors


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