laurent22/joplin · error · Error
Unsupported link format.
Error message
Unsupported link format.
What it means
Thrown when a link starts with 'joplin://' or ':/' (so it enters the internal-link branch) but neither parseResourceUrl nor parseCallbackUrl can make sense of it. The link claims to be a Joplin internal link but matches no known internal-link grammar.
Source
Thrown at packages/app-mobile/commands/openItem.ts:59
execute: async (_context: CommandContext, link: string) => {
if (!link) throw new Error('Link cannot be empty');
try {
if (link.startsWith('joplin://') || link.startsWith(':/')) {
const parsedResourceUrl = parseResourceUrl(link);
const parsedCallbackUrl = isCallbackUrl(link) ? parseCallbackUrl(link) : null;
if (parsedResourceUrl) {
const { itemId, hash } = parsedResourceUrl;
await openItemById(itemId, hash);
} else if (parsedCallbackUrl) {
const id = parsedCallbackUrl.params.id;
if (!id) {
throw new Error('Missing item ID');
}
await openItemById(id);
} else {
throw new Error('Unsupported link format.');
}
} else if (urlProtocol(link)) {
shim.openUrl(link);
} else {
throw new Error('Unsupported protocol');
}
} catch (error) {
const errorMessage = _('Unsupported link or message: %s.\nError: %s', link, error);
logger.error(errorMessage);
await shim.showErrorDialog(errorMessage);
}
},
};
};
View on GitHub (pinned to 2654b33620)
Solutions
- Regenerate the internal link via Joplin's 'copy note link' / 'copy resource link'.
- Compare the failing link against a known-good one to spot structural differences.
- If introducing a new link grammar, add a parser branch before the else.
- Catch the error and surface the link verbatim to the user for correction.
Example fix
// before
} else {
throw new Error('Unsupported link format.');
}
// after — show the link so the user can correct it
} else {
throw new Error(_('Unsupported link format: %s', link));
} Defensive patterns
Strategy: validation
Validate before calling
const isInternal = link.startsWith('joplin://') || link.startsWith(':/');
const resourceUrl = isInternal ? parseResourceUrl(link) : null;
const callbackUrl = isInternal && isCallbackUrl(link) ? parseCallbackUrl(link) : null;
if (isInternal && !resourceUrl && !callbackUrl) {
// unsupported internal format — do not navigate
return;
} Type guard
function isRecognizedInternalLink(link: string): boolean {
if (!link.startsWith('joplin://') && !link.startsWith(':/')) return false;
return !!parseResourceUrl(link) || (isCallbackUrl(link) && !!parseCallbackUrl(link));
} Try / catch
try {
await CommandService.instance().execute('openItem', link);
} catch (e) {
if (e.message === 'Unsupported link format.') {
// show the link verbatim and ask the user to correct it
} else throw e;
} Prevention
- Generate internal links via Joplin's built-in copy-link actions.
- Compare a failing link to a known-good one to spot structural breaks.
- Add a parser branch before the else when introducing new link grammars.
When it happens
Trigger: link.startsWith('joplin://') or ':/' is true; parseResourceUrl(link) returns null; isCallbackUrl(link) is false (or parseCallbackUrl yields null). The else branch throws 'Unsupported link format.'.
Common situations: A typo'd or partially-typed internal link; a link using a joplin:// sub-scheme the parser doesn't recognize; a future link format not yet handled; the link was edited manually and broke its structure.
Related errors
- Unsupported protocol
- Item not found: ${itemId}
- Unsupported item type for links: ${item.type_}
- Link cannot be empty
- Missing item ID
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/80dc9bf661a37209.
Report an issue: GitHub.