{"record":{"id":"32bbd8b6dc3b8a43","repo":"linshenkx/prompt-optimizer","slug":"customheadererror","errorCode":null,"errorMessage":"customHeaderError","messagePattern":"customHeaderError","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/ui/src/composables/model/useTextModelManager.ts","lineNumber":905,"sourceCode":"      modelMeta,\n      connectionConfig,\n      paramOverrides: { ...(form.value.paramOverrides ?? {}) }\n    } as TextModelConfig\n\n    await modelManager.addModel(modelKey, newConfig)\n    return modelKey\n  }\n\n  const saveForm = async () => {\n    if (isSaving.value) return null\n    if (!canSaveForm.value) {\n      throw new Error('No provider or model selected')\n    }\n    isSaving.value = true\n    try {\n      const customHeaderError = getCustomHeaderValidationMessage()\n      if (customHeaderError) {\n        throw new Error(customHeaderError)\n      }\n\n      const savedId = editingModelId.value ? await updateExistingModel() : await createNewModel()\n      await loadModels()\n      return savedId\n    } finally {\n      isSaving.value = false\n    }\n  }\n\n  const testFormConnection = async () => {\n    // 使用 canTestFormConnection 的完整校验逻辑\n    if (!canTestFormConnection.value) return\n\n    isTestingFormConnection.value = true\n    formConnectionStatus.value = { type: 'info', message: t('modelManager.testing') }\n\n    try {","sourceCodeStart":887,"sourceCodeEnd":923,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/composables/model/useTextModelManager.ts#L887-L923","documentation":"saveForm runs getCustomHeaderValidationMessage() and rethrows its message when custom HTTP headers on the form are malformed. This is not a fixed message: the thrown text is the validation message produced for the invalid header entry (duplicate names, empty keys, invalid characters, or bad JSON where applicable).","triggerScenarios":"Adding custom headers in the model form with an empty header name, a name/value containing invalid characters, whitespace-only entries, or duplicate header names. Any non-empty return from the validator triggers the throw before save.","commonSituations":"Pasting a header row with trailing newline creating an empty entry; duplicating an existing header; copy-pasting 'Authorization' twice; leaving a header row half-filled after adding a row in the UI.","solutions":["Fix or remove the offending custom header row (fill both name and value, remove duplicates/empties)","Trim header names/values before validation","If headers are unused, clear the custom headers list entirely","Add row-level validation in the UI so the user sees which row is invalid"],"exampleFix":"// before\nAuthorization: \nAuthorization: Bearer x\n\n// after\nAuthorization: Bearer x","handlingStrategy":"validation","validationCode":"const headers = form.value.customHeaders.filter((h) => h.name.trim() && h.value.trim())\nconst names = headers.map((h) => h.name.trim().toLowerCase())\nif (new Set(names).size !== names.length) { /* duplicate header; fix before save */ }","typeGuard":"const isValidHeader = (h: { name?: string; value?: string }): h is { name: string; value: string } =>\n  typeof h.name === 'string' && h.name.trim().length > 0 && typeof h.value === 'string' && h.value.trim().length > 0","tryCatchPattern":"try { await saveForm() } catch (e) { if (String(e?.message) !== 'No provider or model selected' && /header/i.test(String(e?.message))) { highlightHeaderRow() } else throw e }","preventionTips":["Trim header names and values on input","Remove empty header rows automatically on blur","Flag duplicate header names in the UI"],"tags":["custom-headers","form-validation","http-headers"],"backgroundTag":"header-validation-failed","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}