laurent22/joplin · error
Note lock is not enabled
Error message
Note lock is not enabled
What it means
Note locking is an opt-in feature; setNoteLockState refuses any enable/disable transition when the feature flag is off. The UI hides these actions when locking is disabled, but commands can still be invoked via keyboard or plugins, so this check fails closed.
Solutions
- Enable note lock in the application settings before attempting to change a note's lock state.
- Gate the command invocation behind an isNoteLockEnabled() check in your plugin/UI.
- Hide or disable the command registration when the feature is off.
- Catch the error and surface a 'feature not enabled' message to the user.
Example fix
// before
await joplin.commands.execute('enableNoteLock', noteId);
// after
if (isNoteLockEnabled()) await joplin.commands.execute('enableNoteLock', noteId); Defensive patterns
Strategy: validation
Validate before calling
if (isNoteLockEnabled()) await joplin.commands.execute('enableNoteLock', noteId); Type guard
const canLock = (): boolean => isNoteLockEnabled();
Try / catch
try { await setNoteLockState(...); } catch (e) { if (e.message === 'Note lock is not enabled') promptEnableNoteLock(); else throw e; } Prevention
- Check the feature flag before registering or invoking lock commands
- Keep UI affordances and command availability in sync
- Document the prerequisite in plugin command descriptions
When it happens
Trigger: Calling enableNoteLock or disableNoteLock (directly or via command/plugin API) while `isNoteLockEnabled()` returns false — i.e. the note-lock feature has not been turned on in settings.
Common situations: Plugins or users invoking the note-lock commands before enabling the feature; keyboard shortcuts registered against hidden commands.
Related errors
- Cannot change the note lock of a conflict note
- Cannot change the note lock of a deleted note
- No such note
- Ambiguous notebook " ". Please use notebook id instead -…
- Ambiguous notebook " ". Please use short notebook id…
AI-assisted analysis of laurent22/joplin@981a03c5c9 (2026-09-17).
Data as JSON: /api/errors/7343f45702c0e326.
Report an issue: GitHub.
Appendix: source
Thrown at packages/lib/services/noteLock/setNoteLockState.ts:15
import { ModelType } from '../../BaseModel';
import BaseItem from '../../models/BaseItem';
import ItemChange from '../../models/ItemChange';
import Note from '../../models/Note';
import Setting from '../../models/Setting';
import { itemIsReadOnlySync, ItemSlice } from '../../models/utils/readOnly';
import { NoteEntity } from '../database/types';
import eventManager, { EventName } from '../../eventManager';
import isNoteLockEnabled from './isNoteLockEnabled';
import NoteLockSession from './NoteLockSession';
// The UI hides the enable/disable actions for these cases, but the commands can also be
// invoked directly (keyboard, plugins), so the transitions fail closed here too.
const checkCanChangeLockState = (note: NoteEntity, noteId: string) => {
if (!isNoteLockEnabled()) throw new Error('Note lock is not enabled');
if (!note) throw new Error(`No such note: ${noteId}`);
if (note.deleted_time) throw new Error('Cannot change the note lock of a deleted note');
if (note.is_conflict) throw new Error('Cannot change the note lock of a conflict note');
if (itemIsReadOnlySync(ModelType.Note, ItemChange.SOURCE_UNSPECIFIED, note as ItemSlice, Setting.value('sync.userId'), BaseItem.syncShareCache)) throw new Error('Cannot change the note lock of a read-only note');
if (!NoteLockSession.instance().isUnlocked()) throw new Error('Cannot change the note lock while the session is locked');
};
const validationFields = ['id', 'is_locked', 'deleted_time', 'is_conflict', 'share_id'];
// These only validate and emit: the note screen listens for the event and persists the
// change with a scheduled gated save.
export const enableNoteLock = async (noteId: string) => {
const note = await Note.load(noteId, { fields: validationFields });
checkCanChangeLockState(note, noteId);
if (note.is_locked) throw new Error(`Note is already locked: ${noteId}`);
eventManager.emit(EventName.NoteLockNoteStateChange, { noteId, isLocked: true });
};
View on GitHub (pinned to 981a03c5c9)