{"record":{"id":"bcaa2f1c913af36c","repo":"yikart/AiToEarn","slug":"errors-savefailed","errorCode":null,"errorMessage":"errors.saveFailed","messagePattern":"errors\\.saveFailed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"project/aitoearn-web/src/app/layout/ConfigManagerDialog/index.tsx","lineNumber":508,"sourceCode":"\n  const saveConfig = useCallback(async (action: LoadingAction = 'save') => {\n    const editableConfig = getEditableConfig()\n    if (!editableConfig)\n      return false\n    const submittableConfig = stripInsertedRelayPlaceholder(editableConfig, insertedRelayPath, serviceTarget)\n\n    setLoadingAction(action)\n    setError(null)\n    setSuccessMessage('')\n\n    try {\n      const valid = await validateConfig(action, editableConfig)\n      if (!valid)\n        return false\n\n      const response = await saveConfigEditorConfigApi({ config: submittableConfig }, serviceTarget, true)\n      if (!response || response.code !== 0) {\n        throw new Error(getResponseMessage(response) || t('errors.saveFailed'))\n      }\n\n      const normalizedConfig = ensureRelayConfig(submittableConfig, serviceTarget)\n      setConfig(normalizedConfig.config)\n      setOriginalConfig(normalizedConfig.config)\n      setJsonText(formatJsonConfig(normalizedConfig.config))\n      setInsertedRelayPath(normalizedConfig.insertedRelayPath)\n      if (action === 'save') {\n        setSuccessMessage(t('messages.saveSuccess'))\n        toast.success(t('messages.saveSuccess'))\n      }\n      return true\n    }\n    catch (saveError) {\n      setError({ title: t('errors.saveFailed'), description: getErrorMessage(saveError) })\n      return false\n    }\n    finally {","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-web/src/app/layout/ConfigManagerDialog/index.tsx#L490-L526","documentation":"ConfigManagerDialog's save handler throws this fallback Error when saveConfigEditorConfigApi returns a non-zero code or an empty response without a backend message. The error is caught and surfaced via setError; the new config is not persisted and UI state is not updated.","triggerScenarios":"Calling saveConfigEditorConfigApi({ config: submittableConfig }, serviceTarget, true) after successful local validation, and receiving response === null/undefined or response.code !== 0 with no message.","commonSituations":"Concurrent edit conflict on the remote config; config editor service down for the target; write permission denied by backend; oversized or schema-invalid config payload; token expired between validate and save.","solutions":["Re-check the config is schema-valid for the target service, then retry save","Verify the config editor service for serviceTarget is running and writable","Re-authenticate if the token expired between validate and save","Check backend logs for the save endpoint failure reason"],"exampleFix":"// before\nif (!response || response.code !== 0) {\n  throw new Error(getResponseMessage(response) || t('errors.saveFailed'))\n}\n// after\nif (!response || response.code !== 0) {\n  setError(getResponseMessage(response) || t('errors.saveFailed'))\n  return false\n}","handlingStrategy":"try-catch","validationCode":"await validateConfigEditorConfigApi({ config }, serviceTarget, true) // must return code === 0 before saving","typeGuard":null,"tryCatchPattern":"try {\n  const response = await saveConfigEditorConfigApi({ config }, serviceTarget, true)\n  if (!response || response.code !== 0)\n    throw new Error(getResponseMessage(response) || 'save failed')\n} catch (e) {\n  setError(e instanceof Error ? e.message : 'save failed')\n  // keep editor state so user can retry without losing edits\n}","preventionTips":["Always run validate before save and block save on failure","Preserve the user's edited config on failure for retry","Re-authenticate on 401-like codes before surfacing save failed","Monitor backend save endpoint errors via logs"],"tags":["config","save","api-response"],"backgroundTag":"api-nonzero-response-code","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}