{"record":{"id":"5bd40f10d0218b11","repo":"laurent22/joplin","slug":"only-one-output-directory-can-be-selected","errorCode":null,"errorMessage":"Only one output directory can be selected","messagePattern":"Only one output directory can be selected","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/app-desktop/gui/WindowCommandsAndDialogs/commands/exportPdf.ts","lineNumber":36,"sourceCode":"\t\t\t\tnoteIds = noteIds || context.state.selectedNoteIds;\n\n\t\t\t\tif (!noteIds.length) throw new Error('No notes selected for pdf export');\n\n\t\t\t\tlet path = null;\n\t\t\t\tif (noteIds.length === 1) {\n\t\t\t\t\tpath = await bridge().showSaveDialog({\n\t\t\t\t\t\tfilters: [{ name: _('PDF File'), extensions: ['pdf'] }],\n\t\t\t\t\t\tdefaultPath: await InteropServiceHelper.defaultFilename(noteIds[0], 'pdf'),\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tpath = await bridge().showOpenDialog({\n\t\t\t\t\t\tproperties: ['openDirectory', 'createDirectory'],\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (Array.isArray(path)) {\n\t\t\t\t\tif (path.length > 1) {\n\t\t\t\t\t\tthrow new Error('Only one output directory can be selected');\n\t\t\t\t\t}\n\n\t\t\t\t\tpath = path[0];\n\t\t\t\t}\n\n\t\t\t\tif (!path) return;\n\n\t\t\t\tfor (let i = 0; i < noteIds.length; i++) {\n\t\t\t\t\tconst note = await Note.load(noteIds[i]);\n\n\t\t\t\t\tlet pdfPath = '';\n\n\t\t\t\t\tif (noteIds.length === 1) {\n\t\t\t\t\t\tpdfPath = path;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst n = await InteropServiceHelper.defaultFilename(note.id, 'pdf');\n\t\t\t\t\t\tpdfPath = await shim.fsDriver().findUniqueFilename(`${path}/${n}`);\n\t\t\t\t\t}","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-desktop/gui/WindowCommandsAndDialogs/commands/exportPdf.ts#L18-L54","documentation":"Thrown by exportPdf when, in multi-note export mode, the directory picker returns an array with more than one entry. Electron's showOpenDialog can return multiple selected paths; the export logic requires exactly one output directory.","triggerScenarios":"Exporting 2+ notes to PDF; showOpenDialog({properties:['openDirectory', ...]}) returns an array of length > 1 (user multi-selected directories), triggering the `path.length > 1` guard.","commonSituations":"The user Ctrl/Cmd-clicks multiple folders in the directory picker; the dialog properties allow multiSelection inadvertently; an automated harness passes multiple directory paths.","solutions":["Select a single directory in the picker dialog.","Restrict showOpenDialog properties to disallow multi-selection (remove 'multiSelections' if present).","Take the first entry (path[0]) instead of throwing if multi-select is not intended to be an error.","Pre-validate the returned array length and re-prompt the user."],"exampleFix":"// before\nif (path.length > 1) {\n  throw new Error('Only one output directory can be selected');\n}\n\n// after — coerce to first selection with a notice\nif (path.length > 1) {\n  path = path[0];\n  bridge().showInfoMessageBox(_('Using the first selected directory: %s', path));\n}","handlingStrategy":"validation","validationCode":"if (Array.isArray(path) && path.length > 1) {\n  // either take the first, or re-prompt the user for a single directory\n  path = path[0];\n}\n// proceed with a single path","typeGuard":"function isSingleDirectory(path: string | string[] | null): path is string {\n  return typeof path === 'string' || (Array.isArray(path) && path.length === 1);\n}","tryCatchPattern":"try {\n  await exportRuntime.execute(ctx, noteIds);\n} catch (e) {\n  if (e.message === 'Only one output directory can be selected') {\n    // re-open the dialog with multiSelection disabled\n  } else throw e;\n}","preventionTips":["Configure showOpenDialog without 'multiSelections' for the directory picker.","Coerce the result to a single path or re-prompt instead of throwing.","Validate the returned array length before proceeding."],"tags":["desktop","export","pdf","filesystem","dialog","validation"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}