{"record":{"id":"9e37200cf6d06626","repo":"laurent22/joplin","slug":"link-cannot-be-empty","errorCode":null,"errorMessage":"Link cannot be empty","messagePattern":"Link cannot be empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/commands/openItem.ts","lineNumber":42,"sourceCode":"\tif (!item) {\n\t\tthrow new Error(`Item not found: ${itemId}`);\n\t}\n\n\tif (item.type_ === ModelType.Note) {\n\t\tawait goToNote(itemId, hash);\n\t} else if (item.type_ === ModelType.Resource) {\n\t\tawait showResource(item);\n\t} else if (item.type_ === ModelType.Folder) {\n\t\tawait goToFolder(item.id);\n\t} else {\n\t\tthrow new Error(`Unsupported item type for links: ${item.type_}`);\n\t}\n};\n\nexport const runtime = (): CommandRuntime => {\n\treturn {\n\t\texecute: async (_context: CommandContext, link: string) => {\n\t\t\tif (!link) throw new Error('Link cannot be empty');\n\n\t\t\ttry {\n\t\t\t\tif (link.startsWith('joplin://') || link.startsWith(':/')) {\n\t\t\t\t\tconst parsedResourceUrl = parseResourceUrl(link);\n\t\t\t\t\tconst parsedCallbackUrl = isCallbackUrl(link) ? parseCallbackUrl(link) : null;\n\n\t\t\t\t\tif (parsedResourceUrl) {\n\t\t\t\t\t\tconst { itemId, hash } = parsedResourceUrl;\n\t\t\t\t\t\tawait openItemById(itemId, hash);\n\t\t\t\t\t} else if (parsedCallbackUrl) {\n\t\t\t\t\t\tconst id = parsedCallbackUrl.params.id;\n\t\t\t\t\t\tif (!id) {\n\t\t\t\t\t\t\tthrow new Error('Missing item ID');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait openItemById(id);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new Error('Unsupported link format.');\n\t\t\t\t\t}","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/commands/openItem.ts#L24-L60","documentation":"Thrown at the top of the openItem command's execute() when the link argument is falsy. The command needs a string to parse and dispatch; an empty/null/undefined link cannot be processed.","triggerScenarios":"Calling the openItem command's execute() with link = '' , null, or undefined. The `if (!link)` guard throws before any parsing.","commonSituations":"A programmatic caller (plugin, notification handler) invokes openItem without an argument; a note's link field is empty; a deep-link entry point fires with no URL payload.","solutions":["Ensure the caller always passes a non-empty link string.","Guard at the call site: only invoke openItem when link is truthy.","If the empty link is from a UI element, disable that element when no link is set.","Catch and ignore silently if an empty link is a benign no-op in your flow."],"exampleFix":"// before\nexecute: async (_context, link) => {\n  if (!link) throw new Error('Link cannot be empty');\n\n// after — no-op instead of throw for programmatic callers\nexecute: async (_context, link) => {\n  if (!link) {\n    logger.warn('openItem called with empty link; ignoring.');\n    return;\n  }","handlingStrategy":"validation","validationCode":"if (!link || typeof link !== 'string' || link.trim().length === 0) {\n  // do not call openItem — log and return\n  return;\n}\nawait CommandService.instance().execute('openItem', link);","typeGuard":"function isNonEmptyLink(link: unknown): link is string {\n  return typeof link === 'string' && link.trim().length > 0;\n}","tryCatchPattern":"try {\n  await CommandService.instance().execute('openItem', link);\n} catch (e) {\n  if (e.message === 'Link cannot be empty') {\n    // caller bug — fix the call site rather than retry\n  } else throw e;\n}","preventionTips":["Always validate the link is a non-empty string before invoking openItem.","Disable UI entry points that fire openItem when no link is available.","Treat empty links as a caller-side bug, not a user error."],"tags":["mobile","links","validation","user-input"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}