{"record":{"id":"bf6a4562ba796b59","repo":"laurent22/joplin","slug":"cannot-find-s-bf6a45","errorCode":null,"errorMessage":"Cannot find \"%s\".","messagePattern":"Cannot find \"(.+?)\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-cli/app/command-help.ts","lineNumber":66,"sourceCode":"\t\t\t\t.gui()\n\t\t\t\t.keymap();\n\n\t\t\tconst rows = [];\n\n\t\t\tfor (let i = 0; i < keymap.length; i++) {\n\t\t\t\tconst item = keymap[i];\n\t\t\t\tconst keys = item.keys.map((k: string) => (k === ' ' ? '(SPACE)' : k));\n\t\t\t\trows.push([keys.join(', '), item.command]);\n\t\t\t}\n\n\t\t\tcliUtils.printArray(this.stdout.bind(this), rows);\n\t\t} else if (args.command === 'all') {\n\t\t\tconst commands = this.allCommands();\n\t\t\tconst output = commands.map(c => renderCommandHelp(c));\n\t\t\tthis.stdout(output.join('\\n\\n'));\n\t\t} else if (args.command) {\n\t\t\tconst command = app().findCommandByName(args['command']);\n\t\t\tif (!command) throw new Error(_('Cannot find \"%s\".', args.command));\n\t\t\tthis.stdout(renderCommandHelp(command, stdoutWidth));\n\t\t} else {\n\t\t\tconst commandNames = this.allCommands().map(a => a.name());\n\n\t\t\tthis.stdout(_('Type `help [command]` for more information about a command; or type `help all` for the complete usage information.'));\n\t\t\tthis.stdout('');\n\t\t\tthis.stdout(_('The possible commands are:'));\n\t\t\tthis.stdout('');\n\t\t\tthis.stdout(commandNames.join(', '));\n\t\t\tthis.stdout('');\n\t\t\tthis.stdout(_('In any command, a note or notebook can be referred to by title or ID, or using the shortcuts `$n` or `$b` for, respectively, the currently selected note or notebook. `$c` can be used to refer to the currently selected item.'));\n\t\t\tthis.stdout('');\n\t\t\tthis.stdout(_('To move from one pane to another, press Tab or Shift+Tab.'));\n\t\t\tthis.stdout(_('Use the arrows and page up/down to scroll the lists and text areas (including this console).'));\n\t\t\tthis.stdout(_('To maximise/minimise the console, press \"tc\".'));\n\t\t\tthis.stdout(_('To enter command line mode, press \":\"'));\n\t\t\tthis.stdout(_('To exit command line mode, press ESCAPE'));\n\t\t\tthis.stdout(_('For the list of keyboard shortcuts and config options, type `help keymap`'));","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-cli/app/command-help.ts#L48-L84","documentation":"Thrown by `help <command>` when `app().findCommandByName(args.command)` returns falsy. In practice findCommandByName (app.ts:227) returns the cached command, or requires `command-<name>.js`, or throws a typed `notFound` error if the module is missing. The `if (!command)` guard at command-help.ts:66 is defensive and only fires if the require returns a falsy module export — extremely rare in normal operation; the common 'unknown command' case is reported via the notFound error, not this one.","triggerScenarios":"A command module whose default export is falsy (broken/empty plugin command file loaded into the commands directory); a stale command cache from a downgraded Joplin version.","commonSituations":"Almost never hit by end users; mainly a developer/extension-author concern when hand-editing command files. Typos in command names hit the 'notFound' error path instead.","solutions":["Run `joplin help` (no args) to list the actually-registered command names","Clear the command metadata cache (remove `~/.config/joplin-dev-desktop/cache.json` / cache db entry keyed 'metadata') and retry","If developing a custom command, verify `module.exports = Command;` exports a truthy class","Check for orphaned `command-*.js` files in the app directory that export null/undefined"],"exampleFix":"// before\njoplin help bogusThing   // may print \"No such command\" (notFound) before reaching this guard\n\n// after\njoplin help              // lists valid commands\njoplin help mknote       // use a name from the list","handlingStrategy":"try-catch","validationCode":"import app from './app';\n\nfunction commandExists(name: string): boolean {\n  try {\n    return !!app().findCommandByName(name);\n  } catch (e: any) {\n    if (e?.type === 'notFound') return false;\n    throw e;\n  }\n}\n\nif (!commandExists(args.command)) {\n  console.error(`Unknown command \"${args.command}\". Run \\`help\\` for the list.`);\n}","typeGuard":"const isRegisteredCommand = (name: string): boolean => {\n  try { return !!app().findCommandByName(name); } catch { return false; }\n};","tryCatchPattern":"try {\n  await cli.execCommand(['help', name]);\n} catch (e) {\n  if (e?.type === 'notFound' || /^Cannot find/.test(e.message)) {\n    // list valid commands as a fallback\n    await cli.execCommand(['help']);\n  } else throw e;\n}","preventionTips":["Validate command names against `app().commands()` before calling `help <name>`","Clear the command metadata cache when developing command files","Ensure custom command modules `module.exports = Command` with a truthy class"],"tags":["cli","help","command-registry","defensive"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}