laurent22/joplin · error · Error

No model found at path: ${JSON.stringify(modelFolderPath)}

Error message

No model found at path: ${JSON.stringify(modelFolderPath)}

What it means

Thrown by the whisper provider's `build` when the model folder path passed in does not exist on disk. This is the first existence check before reading `config.json` or `model.bin`, guarding against a missing or partially extracted model bundle.

Source

Thrown at packages/app-mobile/services/voiceTyping/whisper.ts:249

			modelLocalFilepath(),
			whisper.getUuidPath(locale),
			// Legacy model filepath
			join(modelLocalDirectory(), 'whisper_tiny.onnx'),
		];

		for (const path of pathsToRemove) {
			if (await shim.fsDriver().exists(path)) {
				logger.info('Remove', path);
				await shim.fsDriver().remove(path, { recursive: true });
			}
		}
	},
	getUuidPath: () => {
		return join(dirname(modelLocalFilepath()), 'uuid');
	},
	build: async ({ modelPath: modelFolderPath, callbacks, locale }) => {
		logger.debug('Creating Whisper session from path', modelFolderPath);
		if (!await shim.fsDriver().exists(modelFolderPath)) throw new Error(`No model found at path: ${JSON.stringify(modelFolderPath)}`);

		if (Setting.value('env') === Env.Dev) {
			try {
				await testWhisper();
			} catch (error) {
				logger.error('Testing error', error);
				await shim.showErrorDialog(`Test failure: ${error}`);
			}
		}

		const modelPath = join(modelFolderPath, 'model.bin');
		const configJsonPath = join(modelFolderPath, 'config.json');
		const configJson = JSON.parse(await shim.fsDriver().readFile(configJsonPath, 'utf-8'));
		const config = new WhisperConfig(configJson);

		if (!await shim.fsDriver().exists(modelPath)) {
			throw new Error(`Model not found at path ${modelPath}`);
		}

View on GitHub (pinned to 2654b33620)

Solutions

  1. Call `VoiceTyping.download()` (or rely on `build`'s own `isDownloaded` check) before `build`.
  2. If the folder is missing unexpectedly, call `clearDownloads()` then `download()` to re-fetch.
  3. Check available storage before downloading; a full disk aborts the download.

Example fix

// before
await provider.build({ modelPath, callbacks, locale });

// after
if (!await shim.fsDriver().exists(modelPath)) {
  await voiceTyping.download();
}
await provider.build({ modelPath, callbacks, locale });
Defensive patterns

Strategy: validation

Validate before calling

if (!await shim.fsDriver().exists(modelFolderPath)) {
  logger.info('Model folder missing; downloading');
  await voiceTyping.download();
}
await provider.build({ modelPath: modelFolderPath, callbacks, locale });

Type guard

null

Try / catch

try {
  await provider.build({ modelPath, callbacks, locale });
} catch (error) {
  if (/No model found at path/i.test(error.message)) {
    await voiceTyping.clearDownloads();
    await voiceTyping.download();
    return provider.build({ modelPath, callbacks, locale });
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling `whisper.build({ modelPath, ... })` where `modelPath` (a folder) does not exist — e.g. after `download()` failed silently, the cache was cleared, or `getModelPath()` returned a stale path.

Common situations: User cleared app cache; a previous download threw mid-stream and the folder was never created; the unzip step failed and cleaned up in its `finally`; storage migrated/changed between launches.

Related errors


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