{"record":{"id":"c1d4a62dd8c0565b","repo":"laurent22/joplin","slug":"please-select-a-notebook-first","errorCode":null,"errorMessage":"Please select a notebook first.","messagePattern":"Please select a notebook first\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-cli/app/command-ls.ts","lineNumber":65,"sourceCode":"\t\t\tqueryOptions.orderByDir = 'ASC';\n\t\t}\n\t\tif (options.reverse === true) queryOptions.orderByDir = queryOptions.orderByDir === 'ASC' ? 'DESC' : 'ASC';\n\t\tqueryOptions.caseInsensitive = true;\n\t\tif (options.type) {\n\t\t\tqueryOptions.itemTypes = [];\n\t\t\tif (options.type.indexOf('n') >= 0) queryOptions.itemTypes.push('note');\n\t\t\tif (options.type.indexOf('t') >= 0) queryOptions.itemTypes.push('todo');\n\t\t}\n\t\tif (pattern) queryOptions.titlePattern = pattern;\n\t\tqueryOptions.uncompletedTodosOnTop = Setting.value('uncompletedTodosOnTop');\n\n\t\tlet modelType = null;\n\t\tif (pattern === '/' || !app().currentFolder()) {\n\t\t\tqueryOptions.includeConflictFolder = true;\n\t\t\titems = await Folder.all(queryOptions);\n\t\t\tmodelType = Folder.modelType();\n\t\t} else {\n\t\t\tif (!app().currentFolder()) throw new Error(_('Please select a notebook first.'));\n\t\t\titems = await Note.previews(app().currentFolder().id, queryOptions);\n\t\t\tmodelType = Note.modelType();\n\t\t}\n\n\t\tif (options.format && options.format === 'json') {\n\t\t\tthis.stdout(JSON.stringify(items));\n\t\t} else {\n\t\t\tlet hasTodos = false;\n\t\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\t\tconst item = items[i];\n\t\t\t\tif ((item as NoteEntity).is_todo) {\n\t\t\t\t\thasTodos = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst seenTitles = [];\n\t\t\tconst rows = [];","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-cli/app/command-ls.ts#L47-L83","documentation":"Thrown by `ls` inside the else-branch (pattern is not `/` AND a current notebook exists at the top check). The inner `if (!app().currentFolder())` guard is effectively dead code: the outer condition at line 60 (`pattern === '/' || !app().currentFolder()`) already routed the no-folder case to the folder-listing branch, and no await runs between the two checks, so currentFolder cannot become null in between. If ever hit, it means currentFolder was set to a falsy value mid-call.","triggerScenarios":"Not reachable through normal CLI usage because of the upstream short-circuit at line 60. Would only fire if a concurrent `switchCurrentFolder(null)` ran between the outer check and the inner check (single-threaded JS makes this impossible here).","commonSituations":"Never observed in practice; the error exists as a defensive invariant. Users hitting \"no notebook\" while listing notes are actually seeing folder output instead (the outer branch).","solutions":["Select a notebook: `joplin use <notebook>` then `joplin ls`","If you genuinely see this message, file a bug — the upstream guard should have prevented it","Run `joplin ls /` explicitly to list notebooks when unsure of selection state"],"exampleFix":"// before\njoplin ls              // when no notebook is selected, lists folders instead (outer branch)\n\n// after\njoplin use Personal\njoplin ls              // lists notes in Personal","handlingStrategy":"validation","validationCode":"import app from './app';\n\n// the throw is effectively unreachable (outer branch handles it), but guard anyway:\nfunction assertNotebookSelectedForLs(): void {\n  if (!app().currentFolder()) {\n    throw new Error('Select a notebook first (`use <notebook>`), or run `ls /` to list notebooks.');\n  }\n}","typeGuard":"const hasSelectedNotebook = (): boolean => !!app().currentFolder();","tryCatchPattern":null,"preventionTips":["Treat `ls` with no notebook as `ls /` (the outer branch already does this)","If this message ever appears, report it — it indicates a logic regression","Use `use <notebook>` to set the active notebook before listing notes"],"tags":["cli","ls","notebook","defensive","unreachable"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}