laurent22/joplin · error · Error
Cannot find "%s".
Error message
Cannot find "%s".
What it means
Thrown by the `tag` command when `tag-command === 'remove'` and the tag specified by `args.tag` could not be loaded (the earlier `app().loadItem(ModelType.Tag, ...)` returned null). The check runs before the add/remove branches so that a non-existent tag aborts removal early. `%s` is replaced with the requested tag title.
Source
Thrown at packages/app-cli/app/command-tag.ts:37
return [['-l, --long', _('Use long list format. Format is ID, NOTE_COUNT (for notebook), DATE, TODO_CHECKED (for to-dos), TITLE')]];
}
public override async action(args: { 'tag-command': string; tag?: string; note?: string; options: { long?: boolean } }) {
let tag: TagEntity | null = null;
const options = args.options;
// app.loadItem's parameter type is narrower (Note | Folder | 'folderOrNote') than the
// runtime support, which falls through to BaseItem.itemClass(type).loadByTitle for other
// types. ModelType.Tag is one of those — cast to satisfy the type checker.
if (args.tag) tag = await app().loadItem(ModelType.Tag as ModelType.Note, args.tag);
let notes: NoteEntity[] = [];
if (args.note) {
notes = await app().loadItems(ModelType.Note, args.note);
}
const command = args['tag-command'];
if (command === 'remove' && !tag) throw new Error(_('Cannot find "%s".', args.tag));
if (command === 'add') {
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
if (!tag) tag = await Tag.save({ title: args.tag }, { userSideValidation: true });
for (let i = 0; i < notes.length; i++) {
await Tag.addNote(tag.id, notes[i].id);
}
} else if (command === 'remove') {
if (!tag) throw new Error(_('Cannot find "%s".', args.tag));
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
for (let i = 0; i < notes.length; i++) {
await Tag.removeNote(tag.id, notes[i].id);
}
} else if (command === 'list') {
if (tag) {
const notes: NoteEntity[] = await Tag.notes(tag.id);
notes.map(note => {
let line = '';View on GitHub (pinned to 2654b33620)
Solutions
- List tags with `:tag list` to confirm the exact title and retry.
- Sync first if the tag may exist only on another device.
- If you only want to ensure the tag is gone, the error confirms it is — no further action needed.
Example fix
// before
if (command === 'remove' && !tag) throw new Error(_('Cannot find "%s".', args.tag));
// after - hint at available tags
if (command === 'remove' && !tag) throw new Error(_('Cannot find tag "%s". Run \`tag list\` to see existing tags.', args.tag)); Defensive patterns
Strategy: validation
Validate before calling
const tag = await app().loadItem(ModelType.Tag as ModelType.Note, tagName);
if (subcommand === 'remove' && !tag) {
throw new Error(`Tag "${tagName}" not found. Run \`tag list\`.`);
} Type guard
const isExistingTag = (t: TagEntity | null): t is TagEntity => t !== null;
Try / catch
try {
await app().executeCommand(['tag', 'remove', tagName, noteTitle]);
} catch (e) {
if (/Cannot find/.test(e.message) && /* tag context */) { /* re-list tags */ }
else throw e;
} Prevention
- List tags before removing one to copy the exact title.
- Sync first if the tag may exist only on another device.
- For removal, an error here means the tag is already gone — treat as success.
When it happens
Trigger: Running `:tag remove <tag> [<note>]` where `<tag>` does not match any Tag.title in the database. Because removal requires an existing tag, a missing tag is a hard error rather than a no-op.
Common situations: Typo in the tag name; tag was renamed or deleted; referencing a tag from another profile; tags not yet synced from the server.
Related errors
- Cannot find "%s".
- Cannot find "%s".
- No share found for folder ${folderId}
- No share found for folder ${folder.id}
- No recipient found with email ${email}
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/a04431fff953d5a1.
Report an issue: GitHub.