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

  1. Regenerate the internal link via Joplin's 'copy note link' / 'copy resource link'.
  2. Compare the failing link against a known-good one to spot structural differences.
  3. If introducing a new link grammar, add a parser branch before the else.
  4. 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

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


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/80dc9bf661a37209. Report an issue: GitHub.