laurent22/joplin · error · Error
Format "${format}" can only be exported to a file
Error message
Format "${format}" can only be exported to a file What it means
Thrown by exportFolders when the chosen format is 'jex' (a single-file archive) but the target path resolves to an existing directory. JEX export must write one bundle file; writing into a directory is structurally unsupported, so the command rejects it before invoking InteropService.export.
Source
Thrown at packages/app-desktop/commands/exportFolders.ts:26
};
export const runtime = (): CommandRuntime => {
return {
// "targetPath" should be a file for JEX export (because the format can
// contain multiple folders) or a directory otherwise.
execute: async (_context: CommandContext, folderIds: string[], format: ExportModuleOutputFormat, targetPath: string) => {
const exportOptions: ExportOptions = {
sourceFolderIds: folderIds,
path: targetPath,
format: format,
target: FileSystemItem.Directory,
};
const targetMustBeFile = format === 'jex';
const targetIsDir = await shim.fsDriver().isDirectory(targetPath);
if (targetMustBeFile && targetIsDir) {
throw new Error(`Format "${format}" can only be exported to a file`);
}
if (format === 'jex' || !targetIsDir) {
exportOptions.target = FileSystemItem.File;
}
return InteropService.instance().export(exportOptions);
},
};
};
View on GitHub (pinned to 2654b33620)
Solutions
- Provide a file path (ending in .jex) as targetPath instead of a directory.
- In the UI, ensure the save dialog uses a 'save file' picker (not 'open directory') when format is jex.
- Switch format to a per-note format (e.g. md, html) if exporting into a directory is desired.
- Validate targetPath with isDirectory() upstream and prompt for a filename.
Example fix
// before
if (targetMustBeFile && targetIsDir) {
throw new Error(`Format "${format}" can only be exported to a file`);
}
// after — name the specific format and what was given
if (targetMustBeFile && targetIsDir) {
throw new Error(`Format "${format}" requires a file path, but "${targetPath}" is a directory.`);
} Defensive patterns
Strategy: validation
Validate before calling
const targetMustBeFile = format === 'jex';
const targetIsDir = await shim.fsDriver().isDirectory(targetPath);
if (targetMustBeFile && targetIsDir) {
// prompt user for a .jex file path instead
return;
}
await CommandService.instance().execute('exportFolders', folderIds, format, targetPath); Type guard
function isValidExportTarget(format: string, isDir: boolean): boolean {
if (format === 'jex' && isDir) return false;
return true;
} Try / catch
try {
await exportRuntime.execute(ctx, folderIds, format, targetPath);
} catch (e) {
if (e.message.includes('can only be exported to a file')) {
// re-prompt for a file path
} else throw e;
} Prevention
- For JEX export, always pass a file path; reserve directory targets for per-note formats.
- Validate targetPath with isDirectory() upstream and choose the dialog type accordingly.
- When building the UI, use a save-file picker for jex and an open-directory picker for others.
When it happens
Trigger: Calling the exportFolders command with format='jex' and a targetPath that shim.fsDriver().isDirectory() reports as a directory. The targetMustBeFile flag is derived from format==='jex'.
Common situations: User selects an existing folder in the save dialog for a JEX export; the default path points at a directory; a script passes a directory path when expecting a .jex file path.
Related errors
- Only one output directory can be selected
- No notes selected for pdf export
- HTML export is not supported. Please use the desktop applica
- Cannot find "%s".
- Locked notes cannot be opened in an external editor
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/77c7b978ee0a8544.
Report an issue: GitHub.