{"record":{"id":"4970f78a3e927580","repo":"mudler/LocalAI","slug":"http-response-status-4970f7","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/http/react-ui/src/pages/ModelEditor.jsx","lineNumber":345,"sourceCode":"    try {\n      if (isCreateMode) {\n        // In create mode, import the YAML as a new config\n        await modelsApi.importConfig(yamlText, 'application/x-yaml')\n        addToast('Model created successfully', 'success')\n        try {\n          const parsed = YAML.parse(yamlText)\n          if (parsed?.name) navigate(`/app/model-editor/${encodeURIComponent(parsed.name)}`, { replace: true, state: backState })\n          else navigate(backState ? backState.from : '/app/manage')\n        } catch { navigate(backState ? backState.from : '/app/manage') }\n      } else {\n        const response = await fetch(apiUrl(`/models/edit/${encodeURIComponent(name)}`), {\n          method: 'POST',\n          headers: { 'Content-Type': 'application/x-yaml' },\n          body: yamlText,\n        })\n        const data = await response.json()\n        if (!response.ok || data.success === false) {\n          throw new Error(data.error || `HTTP ${response.status}`)\n        }\n        // Refresh interactive state from saved YAML\n        setSavedYamlText(yamlText)\n        let parsedName = null\n        try {\n          const parsed = YAML.parse(yamlText)\n          parsedName = parsed?.name ?? null\n          const flat = flattenConfig(parsed || {}, leafPaths)\n          setValues(flat)\n          setInitialValues(structuredClone(flat))\n          setActiveFieldPaths(new Set(Object.keys(flat)))\n        } catch { /* ignore parse failure */ }\n        setTabSwitchWarning(false)\n        addToast('Config saved', 'success')\n        // When the model was renamed via the YAML `name:` field, the current\n        // editor URL points at a name that no longer exists on the backend.\n        // Redirect so refreshes and subsequent saves hit the new name.\n        if (parsedName && parsedName !== name) {","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/core/http/react-ui/src/pages/ModelEditor.jsx#L327-L363","documentation":"Thrown by ModelEditor's save path when POST /models/edit/<name> (raw YAML body, Content-Type application/x-yaml) returns a non-OK status or JSON body with success === false and no error field. The HTTP status fallback string is only used when data.error is absent. On success the component re-parses the YAML to refresh interactive state (setSavedYamlText, flattenConfig), and navigates back to the model list or the previous location.","triggerScenarios":"Posting YAML that fails server-side validation (unknown backend name, bad parameters block), a model name in the URL that does not exist on disk (404), auth failure (401), or a JSON error body without an error field.","commonSituations":"Editing a model config whose file was deleted outside the UI; switching backend in YAML to one not installed; expired session; typo in parameters.model path rejected by the server.","solutions":["Read the response body in devtools — data.error usually contains the exact validation message; only trust HTTP <status> when it is empty","Fix the YAML field named in the server error (most often backend or parameters.model)","If 401/403, re-login and re-submit; the editor preserves yamlText","If 404, confirm the model name in the URL matches an installed model config"],"exampleFix":"// before\nconst data = await response.json()\nif (!response.ok || data.success === false) {\n  throw new Error(data.error || `HTTP ${response.status}`)\n}\n\n// after: tolerate non-JSON error bodies (proxy HTML error pages)\nlet data = null\ntry { data = await response.json() } catch { /* body not JSON */ }\nif (!response.ok || data?.success === false) {\n  throw new Error(data?.error || `HTTP ${response.status}`)\n}","handlingStrategy":"try-catch","validationCode":"// Cheap client-side YAML gate before the POST (full rules live server-side)\nfunction modelYamlLooksComplete(yamlText, YAML) {\n  try {\n    const cfg = YAML.parse(yamlText)\n    if (!cfg || typeof cfg !== 'object' || !cfg.name) return false\n    const isPipeline = cfg.pipeline && (cfg.pipeline.vad || cfg.pipeline.transcription || cfg.pipeline.tts || cfg.pipeline.llm)\n    return isPipeline || (!!cfg.backend && !!(cfg.parameters && cfg.parameters.model))\n  } catch { return false }\n}","typeGuard":"function isApiError(data) {\n  return !!data && typeof data === 'object' && data.success === false\n}","tryCatchPattern":"let data = null\ntry { data = await response.json() } catch { /* non-JSON body (proxy page) */ }\nif (!response.ok || isApiError(data)) {\n  throw new Error(data?.error || `HTTP ${response.status}`)\n}","preventionTips":["Validate the name/backend/parameters.model triple client-side to catch most rejections before the network round trip","Keep the editor's model name in the URL in sync with the YAML's name field","Parse the response body defensively — error pages from proxies are not JSON"],"tags":["http","yaml","model-config","react-ui"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}