{"record":{"id":"900c8472f51fdd96","repo":"laurent22/joplin","slug":"locked-notes-cannot-be-opened-in-an-external-edito","errorCode":null,"errorMessage":"Locked notes cannot be opened in an external editor","messagePattern":"Locked notes cannot be opened in an external editor","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-desktop/commands/startExternalEditing.ts","lineNumber":24,"sourceCode":"import isNoteLockEnabled from '@joplin/lib/services/noteLock/isNoteLockEnabled';\nimport bridge from '../services/bridge';\n\nexport const declaration: CommandDeclaration = {\n\tname: 'startExternalEditing',\n\tlabel: () => _('Open in external editor'),\n\ticonName: 'icon-share',\n};\n\nexport const runtime = (): CommandRuntime => {\n\treturn {\n\t\texecute: async (context: CommandContext, noteId: string = null) => {\n\t\t\tnoteId = noteId || stateUtils.selectedNoteId(context.state);\n\n\t\t\ttry {\n\t\t\t\tconst note = await Note.load(noteId);\n\t\t\t\t// The enabled condition can be bypassed when the command is executed directly (e.g. via\n\t\t\t\t// toggleExternalEditing): the external editor would get ciphertext, or write plaintext to disk.\n\t\t\t\tif (isNoteLockEnabled() && note.is_locked) throw new Error(_('Locked notes cannot be opened in an external editor'));\n\t\t\t\tvoid ExternalEditWatcher.instance().openAndWatch(note);\n\t\t\t} catch (error) {\n\t\t\t\tbridge().showErrorMessageBox(_('Error opening note in editor: %s', error.message));\n\t\t\t}\n\t\t},\n\t\tenabledCondition: 'oneNoteSelected && !noteIsReadOnly && !noteIsLocked',\n\t};\n};\n","sourceCodeStart":6,"sourceCodeEnd":33,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-desktop/commands/startExternalEditing.ts#L6-L33","documentation":"Thrown by the startExternalEditing command when EEE (Encryption Master Password / note lock) is enabled and the target note has is_locked true. Locked notes are encrypted at rest; opening them in an external editor would either expose ciphertext to the editor process or let plaintext be written to an unencrypted temp file, so the command refuses.","triggerScenarios":"Executing startExternalEditing (directly or via toggleExternalEditing) on a note where isNoteLockEnabled() returns true AND note.is_locked is true. The enabledCondition already excludes locked notes, but a direct execute() call bypasses enabled checks, hence the in-body guard.","commonSituations":"A toolbar/keyboard shortcut wired directly to execute() instead of going through the enabled-condition gate; programmatic command invocation from a plugin; the note was locked after the menu was rendered so the enabled state is stale.","solutions":["Unlock the note (remove is_locked) before launching the external editor.","Disable note locking (clear the master password / lock feature) if external editing is required for that note.","Route calls through the command service so enabledCondition (`!noteIsLocked`) disables the action rather than throwing.","In a plugin, check note.is_locked and isNoteLockEnabled() before calling execute()."],"exampleFix":"// before\nif (isNoteLockEnabled() && note.is_locked) throw new Error(_('Locked notes cannot be opened in an external editor'));\n\n// after — guard before invoking, surface a user-actionable hint\nif (isNoteLockEnabled() && note.is_locked) {\n  throw new Error(_('Locked notes cannot be opened in an external editor. Unlock the note first.'));\n}","handlingStrategy":"validation","validationCode":"const note = await Note.load(noteId);\nif (isNoteLockEnabled() && note?.is_locked) {\n  // ask user to unlock, or abort — do not call startExternalEditing\n  return;\n}\nawait CommandService.instance().execute('startExternalEditing', noteId);","typeGuard":"function canOpenInExternalEditor(note: NoteEntity | null): boolean {\n  return !!note && !(isNoteLockEnabled() && note.is_locked);\n}","tryCatchPattern":"try {\n  await CommandService.instance().execute('startExternalEditing', noteId);\n} catch (e) {\n  if (e.message.includes('Locked notes')) {\n    // prompt the user to unlock the note first\n  } else throw e;\n}","preventionTips":["Route external-edit invocations through the command service so enabledCondition (!noteIsLocked) disables the action.","Check isNoteLockEnabled() and note.is_locked before any direct execute() call.","Unlock notes before launching external editing when EEE is on."],"tags":["desktop","encryption","external-editor","security","validation"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}