laurent22/joplin · error · Error

Item not found: ${itemId}

Error message

Item not found: ${itemId}

What it means

Thrown by openItemById when BaseItem.loadItemById(itemId) returns a falsy value. The command resolved a link to an item ID but no record with that ID exists (or is not visible) in the local database, so navigation cannot proceed.

Source

Thrown at packages/app-mobile/commands/openItem.ts:25

import BaseItem from '@joplin/lib/models/BaseItem';
import { BaseItemEntity } from '@joplin/lib/services/database/types';
import { ModelType } from '@joplin/lib/BaseModel';
import showResource from './util/showResource';
import { isCallbackUrl, parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import goToFolder from './util/goToFolder';

const logger = Logger.create('openItemCommand');

export const declaration: CommandDeclaration = {
	name: 'openItem',
};

const openItemById = async (itemId: string, hash?: string) => {
	logger.info(`Navigating to item ${itemId}`);
	const item: BaseItemEntity = await BaseItem.loadItemById(itemId);

	if (!item) {
		throw new Error(`Item not found: ${itemId}`);
	}

	if (item.type_ === ModelType.Note) {
		await goToNote(itemId, hash);
	} else if (item.type_ === ModelType.Resource) {
		await showResource(item);
	} else if (item.type_ === ModelType.Folder) {
		await goToFolder(item.id);
	} else {
		throw new Error(`Unsupported item type for links: ${item.type_}`);
	}
};

export const runtime = (): CommandRuntime => {
	return {
		execute: async (_context: CommandContext, link: string) => {
			if (!link) throw new Error('Link cannot be empty');

View on GitHub (pinned to 2654b33620)

Solutions

  1. Sync to ensure the referenced item is downloaded to this device.
  2. Verify the itemId in the link matches an existing record (query the DB).
  3. Remove or fix the stale link in the source note.
  4. Catch the error and show a 'link target not found' message instead of crashing.

Example fix

// before
const item = await BaseItem.loadItemById(itemId);
if (!item) throw new Error(`Item not found: ${itemId}`);

// after — softer UX with sync hint
const item = await BaseItem.loadItemById(itemId);
if (!item) throw new Error(`Item not found: ${itemId}. It may have been deleted or not yet synced.`);
Defensive patterns

Strategy: validation

Validate before calling

const item = await BaseItem.loadItemById(itemId);
if (!item) {
  // maybe not synced yet — sync then retry, or inform user
  await reg.syncAll();
  if (!(await BaseItem.loadItemById(itemId))) return;
}

Type guard

function itemExists(item: BaseItemEntity | null): item is BaseItemEntity {
  return item != null;
}

Try / catch

try {
  await openItemById(itemId);
} catch (e) {
  if (e.message.startsWith('Item not found')) {
    // sync, then retry once; else show 'link target unavailable'
  } else throw e;
}

Prevention

When it happens

Trigger: A joplin:// or :/ link (resource URL or callback URL) carries an itemId; openItemById loads it; loadItemById returns null/undefined; the `if (!item)` guard throws.

Common situations: Following a link to a note/resource/folder that was deleted; the item has not yet synced to this device; the link contains a stale or wrong ID; cross-profile links where the ID exists in another profile's DB.

Related errors


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