{"record":{"id":"dfebb7c15e0cd6e3","repo":"Stirling-Tools/Stirling-PDF","slug":"unsupported-conversion-format","errorCode":null,"errorMessage":"Unsupported conversion format","messagePattern":"Unsupported conversion format","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/hooks/tools/convert/useConvertOperation.ts","lineNumber":254,"sourceCode":"  const fallbackFilename = `${originalName}.${targetExtension}`;\n\n  return createFileFromApiResponse(responseData, headers, fallbackFilename);\n};\n\n// Static processor that can be used by both the hook and automation executor\nexport const convertProcessor = async (\n  parameters: ConvertParameters,\n  selectedFiles: File[],\n): Promise<CustomProcessorResult> => {\n  const processedFiles: File[] = [];\n\n  // Map PDF/X to use PDF/A endpoint\n  const actualToExtension =\n    parameters.toExtension === \"pdfx\" ? \"pdfa\" : parameters.toExtension;\n  const endpoint = getEndpointUrl(parameters.fromExtension, actualToExtension);\n\n  if (!endpoint) {\n    throw new Error(\"Unsupported conversion format\");\n  }\n\n  // Convert-specific routing logic: decide batch vs individual processing\n  // For PDF/X, we want to treat it similar to PDF/A (separate processing)\n  const isSeparateProcessing = shouldProcessFilesSeparately(selectedFiles, {\n    ...parameters,\n    toExtension: actualToExtension, // Use the mapped extension for decision logic\n  });\n\n  if (isSeparateProcessing) {\n    // Individual processing for complex cases (PDF→image, smart detection, etc.)\n    for (const file of selectedFiles) {\n      try {\n        const formData = buildConvertFormData(parameters, [file]);\n        const response = await apiClient.post(endpoint, formData, {\n          responseType: \"blob\",\n        });\n","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/hooks/tools/convert/useConvertOperation.ts#L236-L272","documentation":"Thrown by the convert processor when `getEndpointUrl(fromExtension, toExtension)` returns a falsy value — i.e. the requested source→target conversion pair has no mapped backend endpoint. The convert tool routes each pair to a specific REST endpoint; an unmapped pair means the backend does not support that conversion.","triggerScenarios":"User (or automation step) requested a from/to combination the frontend converter does not know about (e.g. a brand-new extension not yet wired into getEndpointUrl); pdfx is mapped to pdfa but a different unsupported alias slipped through; file extension detection produced an unexpected value (uppercase, leading dot, unknown format).","commonSituations":"New file format added to the picker but not to the endpoint map; extension normalization bug (case sensitivity, trailing whitespace); user-supplied/automation-provided extension that bypassed the dropdown.","solutions":["Normalize and lowercase fromExtension/toExtension before lookup, and strip leading dots.","Extend getEndpointUrl's mapping to cover the missing pair, or restrict the picker to supported pairs.","Surface the unsupported pair to the user ('Conversion from X to Y is not supported') instead of a generic message.","Add a unit test over the full supported extension matrix so regressions in the map are caught."],"exampleFix":"// before\nconst actualToExtension = parameters.toExtension === \"pdfx\" ? \"pdfa\" : parameters.toExtension;\nconst endpoint = getEndpointUrl(parameters.fromExtension, actualToExtension);\nif (!endpoint) throw new Error(\"Unsupported conversion format\");\n\n// after — normalize and name the unsupported pair\nconst from = parameters.fromExtension.toLowerCase().replace(/^\\./, \"\");\nconst to = (parameters.toExtension === \"pdfx\" ? \"pdfa\" : parameters.toExtension).toLowerCase().replace(/^\\./, \"\");\nconst endpoint = getEndpointUrl(from, to);\nif (!endpoint) throw new Error(`Unsupported conversion format: ${from} → ${to}`);","handlingStrategy":"validation","validationCode":"// Normalize and check the conversion pair is supported before posting\nconst from = String(parameters.fromExtension).toLowerCase().replace(/^\\./, \"\");\nconst to = String(parameters.toExtension).toLowerCase().replace(/^\\./, \"\");\nif (!getEndpointUrl(from, to === \"pdfx\" ? \"pdfa\" : to)) {\n  // do not call convertProcessor; show 'X to Y not supported'\n}","typeGuard":"function isSupportedPair(from: string, to: string): boolean {\n  return !!getEndpointUrl(from.toLowerCase().replace(/^\\./, \"\"), to.toLowerCase().replace(/^\\./, \"\"));\n}","tryCatchPattern":"try {\n  await convertProcessor(parameters, selectedFiles);\n} catch (e) {\n  if (e instanceof Error && e.message === \"Unsupported conversion format\") {\n    toast.error(`Conversion ${parameters.fromExtension} → ${parameters.toExtension} is not supported.`);\n  } else throw e;\n}","preventionTips":["Restrict the picker to pairs present in getEndpointUrl's map.","Normalize (lowercase, strip dots) extensions before lookup.","Unit-test the full supported matrix so map regressions are caught."],"tags":["convert","validation","file-format","routing"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}