{"record":{"id":"cc4a8f78a992e552","repo":"linshenkx/prompt-optimizer","slug":"variables-importer-errors-unsupportedformat","errorCode":null,"errorMessage":"variables.importer.errors.unsupportedFormat","messagePattern":"variables\\.importer\\.errors\\.unsupportedFormat","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/src/components/variable/VariableImporter.vue","lineNumber":246,"sourceCode":"  }\n  return placeholders[textFormat.value] || placeholders.csv\n}\n\nconst getTextInputHelp = (): string => {\n  const helps = {\n    csv: t('variables.importer.csvTextHelp'),\n    txt: t('variables.importer.txtTextHelp')\n  }\n  return helps[textFormat.value] || helps.csv\n}\n\nconst parseVariables = (data: unknown, format: 'csv' | 'txt' = 'csv'): Record<string, string> => {\n  if (format === 'csv') {\n    return parseCsvVariables(data as string)\n  } else if (format === 'txt') {\n    return parseTxtVariables(data as string)\n  }\n  throw new Error(t('variables.importer.errors.unsupportedFormat'))\n}\n\nconst parseCsvVariables = (content: string): Record<string, string> => {\n  const lines = content.trim().split('\\n')\n  if (lines.length < 2) {\n    throw new Error(t('variables.importer.errors.csvMinRows'))\n  }\n  \n  const headers = lines[0].split(',').map(h => h.trim().replace(/\"/g, ''))\n  const nameIndex = headers.findIndex(h => ['name', 'key', 'variable'].includes(h.toLowerCase()))\n  const valueIndex = headers.findIndex(h => ['value', 'val'].includes(h.toLowerCase()))\n  \n  if (nameIndex === -1 || valueIndex === -1) {\n    throw new Error(t('variables.importer.errors.csvRequiredColumns'))\n  }\n  \n  const variables: Record<string, string> = {}\n  ","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/components/variable/VariableImporter.vue#L228-L264","documentation":"This error is thrown by parseVariables when the requested import format is neither 'csv' nor 'txt'. With the union type 'csv' | 'txt' this is effectively unreachable in correct TypeScript code; it fires only when the format value comes from untyped input (e.g. a file extension parsed at runtime) or the type assertion is bypassed.","triggerScenarios":"Calling parseVariables(data, format) with any string other than 'csv' or 'txt' — typically a file extension like 'json' or 'xlsx' derived from the uploaded filename, or a value forced through `as any`.","commonSituations":"UI lets users pick arbitrary files; the extension-to-format mapping (e.g. .csv -> 'csv', .txt -> 'txt') misses a case like '.tsv' or uppercase '.CSV', or JS callers ignore the union type.","solutions":["Restrict the upstream file picker/accept attribute to .csv/.txt and normalize the extension (lowercase, strip dot) before mapping to a format.","If new formats are actually needed, add a parser branch (e.g. 'json') instead of throwing.","Make the format parameter a const-typed literal at call sites so the compiler rejects invalid values."],"exampleFix":"// before\nconst format = file.name.split('.').pop() as 'csv' | 'txt'\nparseVariables(data, format)\n// after\nconst ext = file.name.split('.').pop()?.toLowerCase()\nif (ext !== 'csv' && ext !== 'txt') {\n  notify(t('variables.importer.errors.unsupportedFormat'))\n  return\n}\nparseVariables(data, ext)","handlingStrategy":"validation","validationCode":"const ext = file.name.split('.').pop()?.toLowerCase().replace('.', '')\nif (ext !== 'csv' && ext !== 'txt') {\n  showError(t('variables.importer.errors.unsupportedFormat'))\n  return\n}\nawait parseVariables(await file.text(), ext as 'csv' | 'txt')","typeGuard":"const isImportFormat = (v: unknown): v is 'csv' | 'txt' =>\n  v === 'csv' || v === 'txt'","tryCatchPattern":"try { parseVariables(data, format) } catch (e) { if (e instanceof Error && e.message.includes('unsupportedFormat')) { /* prompt user to pick .csv/.txt */ } else throw e }","preventionTips":["Restrict the file input's accept attribute to '.csv,.txt'.","Normalize extensions (lowercase, strip dot) before mapping to format.","Keep the format parameter typed as the literal union at every call site."],"tags":["vue","file-import","validation","typescript"],"backgroundTag":"unsupported-file-format","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}