{"record":{"id":"77c7b978ee0a8544","repo":"laurent22/joplin","slug":"format-format-can-only-be-exported-to-a-file","errorCode":null,"errorMessage":"Format \"${format}\" can only be exported to a file","messagePattern":"Format \"(.+?)\" can only be exported to a file","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-desktop/commands/exportFolders.ts","lineNumber":26,"sourceCode":"};\n\nexport const runtime = (): CommandRuntime => {\n\treturn {\n\t\t// \"targetPath\" should be a file for JEX export (because the format can\n\t\t// contain multiple folders) or a directory otherwise.\n\t\texecute: async (_context: CommandContext, folderIds: string[], format: ExportModuleOutputFormat, targetPath: string) => {\n\t\t\tconst exportOptions: ExportOptions = {\n\t\t\t\tsourceFolderIds: folderIds,\n\t\t\t\tpath: targetPath,\n\t\t\t\tformat: format,\n\t\t\t\ttarget: FileSystemItem.Directory,\n\t\t\t};\n\n\t\t\tconst targetMustBeFile = format === 'jex';\n\t\t\tconst targetIsDir = await shim.fsDriver().isDirectory(targetPath);\n\n\t\t\tif (targetMustBeFile && targetIsDir) {\n\t\t\t\tthrow new Error(`Format \"${format}\" can only be exported to a file`);\n\t\t\t}\n\n\t\t\tif (format === 'jex' || !targetIsDir) {\n\t\t\t\texportOptions.target = FileSystemItem.File;\n\t\t\t}\n\n\t\t\treturn InteropService.instance().export(exportOptions);\n\t\t},\n\t};\n};\n","sourceCodeStart":8,"sourceCodeEnd":37,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-desktop/commands/exportFolders.ts#L8-L37","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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."],"exampleFix":"// before\nif (targetMustBeFile && targetIsDir) {\n  throw new Error(`Format \"${format}\" can only be exported to a file`);\n}\n\n// after — name the specific format and what was given\nif (targetMustBeFile && targetIsDir) {\n  throw new Error(`Format \"${format}\" requires a file path, but \"${targetPath}\" is a directory.`);\n}","handlingStrategy":"validation","validationCode":"const targetMustBeFile = format === 'jex';\nconst targetIsDir = await shim.fsDriver().isDirectory(targetPath);\nif (targetMustBeFile && targetIsDir) {\n  // prompt user for a .jex file path instead\n  return;\n}\nawait CommandService.instance().execute('exportFolders', folderIds, format, targetPath);","typeGuard":"function isValidExportTarget(format: string, isDir: boolean): boolean {\n  if (format === 'jex' && isDir) return false;\n  return true;\n}","tryCatchPattern":"try {\n  await exportRuntime.execute(ctx, folderIds, format, targetPath);\n} catch (e) {\n  if (e.message.includes('can only be exported to a file')) {\n    // re-prompt for a file path\n  } else throw e;\n}","preventionTips":["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."],"tags":["desktop","export","filesystem","validation","interop"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}