laurent22/joplin · error · Error

No such folder: ${folderId}

Error message

No such folder: ${folderId}

What it means

Thrown by ShareService.unpublishFolder() when Folder.load(folderId) returns null, meaning no folder with that ID exists in the local database. Share operations run entirely against the local Joplin database (not the server), so the folder must have been synced to this device before it can be unpublished. The error is a plain guard clause at the top of the method, before any share state or network call is touched.

Source

Thrown at packages/lib/services/share/ShareService.ts:327

	}

	public async publishFolder(folderId: string): Promise<StateShare> {
		const folder = await Folder.load(folderId);
		if (!folder) throw new Error(`No such folder: ${folderId}`);

		const share = await this.api().exec('POST', 'api/shares', {}, {
			folder_id: folderId,
			type: ShareType.PublishedFolder,
		});

		await this.refreshShares();

		return share;
	}

	public async unpublishFolder(folderId: string): Promise<void> {
		const folder = await Folder.load(folderId);
		if (!folder) throw new Error(`No such folder: ${folderId}`);

		const share = this.shares.find(s => s.type === ShareType.PublishedFolder && s.folder_id === folderId);
		if (!share) throw new Error(`No published share for folder: ${folderId}`);

		const remainingShares = this.shares.filter(s => s.id !== share.id);
		const folderIds = [folderId, ...(await Folder.allChildrenFolders(folderId)).map(f => f.id)];
		const folders = await Folder.loadItemsByIds(folderIds) as FolderEntity[];
		const noteIds = (await Promise.all(folderIds.map(id => Folder.noteIds(id, { includeConflicts: true, includeDeleted: true })))).flat();
		const notes = await Note.loadItemsByIds(noteIds) as NoteEntity[];
		const directlyPublishedNoteIds = new Set(remainingShares
			.filter(s => s.type === ShareType.Note && !!s.note_id)
			.map(s => s.note_id));

		for (const folderItem of folders) {
			await Folder.updateShareStatus({ ...folderItem, type_: ModelType.Folder }, false);
		}

		for (const note of notes) {

View on GitHub (pinned to dc4e0b464e)

Solutions

  1. Verify the folder exists locally first: const folder = await Folder.load(folderId) and bail or re-render if null.
  2. Make sure sync has run to completion on this device so the folder row exists before invoking share operations.
  3. Check the ID source: confirm you are passing the folder's id (not parent_id, not a note id, not a share id).
  4. If the folder was deleted intentionally, treat 'No such folder' as success (nothing left to unpublish) instead of letting it bubble up.

Example fix

// before
await shareService.unpublishFolder(folderId);

// after
const folder = await Folder.load(folderId);
if (!folder) return; // nothing to unpublish locally
await shareService.unpublishFolder(folderId);
Defensive patterns

Strategy: validation

Validate before calling

import { Folder } from '@joplin/lib/models/Folder';

const folder = await Folder.load(folderId);
if (!folder) {
	// Folder is not in the local database: resync, or treat as nothing-to-unpublish
	return;
}

Type guard

const isExistingFolder = (f: FolderEntity | null): f is FolderEntity => !!f;

Try / catch

try {
	await shareService.unpublishFolder(folderId);
} catch (error) {
	if (error instanceof Error && error.message.startsWith('No such folder:')) {
		// Local DB lacks the folder: run a sync and retry once, or accept as no-op
	} else {
		throw error;
	}
}

Prevention

When it happens

Trigger: Calling unpublishFolder(folderId) with an ID that is not in the local folders table: a typo'd/truncated ID, an ID from a different profile or device that was never synced, a folder already deleted locally (e.g. in the trash or removed by a previous sync), or passing a note ID where a folder ID is expected.

Common situations: Scripts or plugins that capture a folder ID, delete/recreate the folder, then call unpublish with the stale ID; UI acting on a stale selection after the folder was deleted on another device and synced away; multi-profile setups where the ID belongs to another profile's database.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of laurent22/joplin@dc4e0b464e (2026-08-21). Data as JSON: /api/errors/34b34a4a9e237353. Report an issue: GitHub.