{"record":{"id":"8baeb1543b5d5d0b","repo":"laurent22/joplin","slug":"no-such-note-noteid","errorCode":null,"errorMessage":"No such note: ${noteId}","messagePattern":"No such note: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/lib/services/share/ShareService.ts","lineNumber":358,"sourceCode":"\n\t\tfor (const folderItem of folders) {\n\t\t\tawait Folder.updateShareStatus({ ...folderItem, type_: ModelType.Folder }, false);\n\t\t}\n\n\t\tfor (const note of notes) {\n\t\t\tawait Note.updateShareStatus({ ...note, type_: ModelType.Note }, directlyPublishedNoteIds.has(note.id));\n\t\t}\n\n\t\tawait Folder.updateAllShareIds(ResourceService.instance(), remainingShares);\n\n\t\t// Clean local state first so next sync can recover if deletion stops midway\n\t\tawait this.deleteShare(share.id);\n\t\tawait this.refreshShares();\n\t}\n\n\tpublic async unshareNote(noteId: string) {\n\t\tconst note = await Note.load(noteId);\n\t\tif (!note) throw new Error(`No such note: ${noteId}`);\n\n\t\tconst noteShares = (await this.loadSharesByItem(noteId))\n\t\t\t.filter(s => s.type === ShareType.Note);\n\n\t\tconst promises: Promise<void>[] = [];\n\n\t\tfor (const share of noteShares) {\n\t\t\tpromises.push(this.deleteShare(share.id));\n\t\t}\n\n\t\tawait Promise.all(promises);\n\n\t\tawait Note.save({\n\t\t\tid: note.id,\n\t\t\tparent_id: note.parent_id,\n\t\t\tis_shared: 0,\n\t\t\tupdated_time: Date.now(),\n\t\t}, {","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/laurent22/joplin/blob/dc4e0b464e43f63851261b0f72fb59d301fc99a7/packages/lib/services/share/ShareService.ts#L340-L376","documentation":"Thrown by ShareService.unshareNote() when Note.load(noteId) returns null, i.e. the note does not exist in the local database. As with the folder guards, share services operate on the local SQLite/IndexedDB content, so the note must already be synced to this device. The check runs before loadSharesByItem() hits the network, so no API call is made when it fires.","triggerScenarios":"Calling unshareNote() with a non-existent or malformed note ID; the note was deleted locally (or moved to trash and purged) before the call; the note exists only on another device/profile and was never synced here; a note ID from a share object (share.note_id) whose underlying note was removed.","commonSituations":"Plugins or scripts that read share.note_id from a stale share list after the source note was deleted; race between a delete action and an unshare action in the UI; test fixtures that build share records without the matching note rows.","solutions":["Validate first: const note = await Note.load(noteId); skip or surface a friendly message if null.","Re-run sync before unsharing so notes created/deleted elsewhere are reflected locally.","Audit where the noteId comes from — if it originates from share.note_id, the share may outlive the note; handle that case explicitly.","If the goal is a clean slate, delete the orphan share directly via deleteShare(share.id) instead of unshareNote()."],"exampleFix":"// before\nawait shareService.unshareNote(noteId);\n\n// after\nconst note = await Note.load(noteId);\nif (!note) return; // note gone locally; nothing to unshare\nawait shareService.unshareNote(noteId);","handlingStrategy":"validation","validationCode":"import { Note } from '@joplin/lib/models/Note';\n\nconst note = await Note.load(noteId);\nif (!note) {\n\treturn; // note absent locally; nothing to unshare\n}","typeGuard":"const isExistingNote = (n: NoteEntity | null): n is NoteEntity => !!n;","tryCatchPattern":"try {\n\tawait shareService.unshareNote(noteId);\n} catch (error) {\n\tif (error instanceof Error && error.message.startsWith('No such note:')) {\n\t\t// Note deleted locally; optionally delete orphan shares via deleteShare(share.id)\n\t} else {\n\t\tthrow error;\n\t}\n}","preventionTips":["Validate note existence with Note.load before calling unshareNote.","When iterating shares, handle share.note_id whose note no longer exists instead of passing it through blindly.","Run sync before batch unshare operations so local note state matches the server."],"tags":["joplin","sharing","note","not-found","local-database"],"backgroundTag":"entity-not-found","analyzedSha":"dc4e0b464e43f63851261b0f72fb59d301fc99a7","analyzedAt":"2026-08-21T12:05:35.031Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}