laurent22/joplin · error · Error
Cannot find "%s".
Error message
Cannot find "%s".
What it means
Thrown by the `todo` CLI command when the note-pattern argument does not match any note in the database. The command calls app().loadItems(ModelType.Note, pattern) and checks that the returned array is non-empty before proceeding; an empty result means no note matched the glob/regex pattern supplied on the command line.
Source
Thrown at packages/app-cli/app/command-todo.ts:22
import { ModelType } from '@joplin/lib/BaseModel';
import Note from '@joplin/lib/models/Note';
import time from '@joplin/lib/time';
import { NoteEntity } from '@joplin/lib/services/database/types';
class Command extends BaseCommand {
public override usage() {
return 'todo <todo-command> <note-pattern>';
}
public override description() {
return _('<todo-command> can either be "toggle" or "clear". Use "toggle" to toggle the given to-do between completed and uncompleted state (If the target is a regular note it will be converted to a to-do). Use "clear" to convert the to-do back to a regular note.');
}
public override async action(args: { 'todo-command': string; 'note-pattern': string }) {
const action = args['todo-command'];
const pattern = args['note-pattern'];
const notes: NoteEntity[] = await app().loadItems(ModelType.Note, pattern);
if (!notes.length) throw new Error(_('Cannot find "%s".', pattern));
for (let i = 0; i < notes.length; i++) {
const note = notes[i];
this.encryptionCheck(note);
let toSave: NoteEntity = {
id: note.id,
};
if (action === 'toggle') {
if (!note.is_todo) {
toSave = Note.toggleIsTodo(note);
} else {
toSave.todo_completed = note.todo_completed ? 0 : time.unixMs();
}
} else if (action === 'clear') {
toSave.is_todo = 0;View on GitHub (pinned to 2654b33620)
Solutions
- Run `ls` (the note-list CLI command) with the same pattern to confirm what matches, then adjust the pattern.
- Verify the note title spelling and case against the database.
- Try the note's ID instead of a title substring as the pattern.
- Ensure the CLI is pointed at the correct profile/database (JOPLIN_PROFILE / --profile).
Example fix
// before
const notes = await app().loadItems(ModelType.Note, pattern);
if (!notes.length) throw new Error(_('Cannot find "%s".', pattern));
// after — surface the count and hint
const notes = await app().loadItems(ModelType.Note, pattern);
if (!notes.length) throw new Error(_('Cannot find "%s". Run `ls <pattern>` to list matching notes.', pattern)); Defensive patterns
Strategy: validation
Validate before calling
// Before invoking the todo command, confirm the pattern matches.
const notes = await app().loadItems(ModelType.Note, pattern);
if (notes.length === 0) {
console.error(`No notes match "${pattern}". Aborting.`);
return;
}
// safe to proceed with toggle/clear Type guard
function patternHasMatches(notes: NoteEntity[]): boolean {
return Array.isArray(notes) && notes.length > 0;
} Try / catch
try {
await command.exec(['todo', 'toggle', pattern]);
} catch (e) {
if (e.message.startsWith('Cannot find')) {
// pattern matched nothing — surface to user, do not retry blindly
} else throw e;
} Prevention
- Always verify a pattern matches via `ls <pattern>` before running todo on it.
- Prefer note IDs over title substrings to avoid ambiguous/empty matches.
- Quote shell arguments to prevent glob expansion corrupting the pattern.
When it happens
Trigger: Running `todo toggle <pattern>` or `todo clear <pattern>` where <pattern> matches zero notes. The match is delegated to app().loadItems which applies the pattern against note titles/IDs; any pattern yielding no hits triggers this.
Common situations: Typo in the note pattern, referencing a note by an old title after a rename, the note existing in a different notebook/sync target, or quoting a pattern with shell special characters that get mangled.
Related errors
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/550efc2504d6501c.
Report an issue: GitHub.