{"record":{"id":"d2d56334a44908a0","repo":"halo-dev/halo","slug":"i18nresources-sendverificationcodefailed-d2d563","errorCode":null,"errorMessage":"i18nResources.sendVerificationCodeFailed","messagePattern":"i18nResources\\.sendVerificationCodeFailed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"application/src/main/resources/templates/login_email-code.html","lineNumber":73,"sourceCode":"\n        const response = await fetch(\"/login/email-code/send\", {\n          method: \"POST\",\n          body: JSON.stringify({ email: email }),\n          headers: {\n            \"Content-Type\": \"application/json\",\n            [headerName]: token,\n          },\n        });\n\n        if (!response.ok) {\n          const json = await response.json();\n          if (json.errors && json.errors.length) {\n            throw new Error(json.errors[0]);\n          }\n          if (json.detail) {\n            throw new Error(json.detail);\n          }\n          throw new Error(i18nResources.sendVerificationCodeFailed);\n        }\n\n        return response;\n      }\n\n      const emailCodeSendButton = document.getElementById(\"emailCodeSendButton\");\n      sendVerificationCode(emailCodeSendButton, sendRequest);\n    });\n  </script>\n</div>\n","sourceCodeStart":55,"sourceCodeEnd":84,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/resources/templates/login_email-code.html#L55-L84","documentation":"Terminal fallback in the email-code login error cascade. The fetch to /login/email-code/send returned !response.ok, but the parsed body has neither a non-empty `errors` array nor a `detail` field, so the code throws a localized generic message (i18nResources.sendVerificationCodeFailed). It is the catch-all for non-RFC-7807 responses — HTML error pages, empty bodies, gateway errors, or malformed JSON-rendered responses.","triggerScenarios":"response.ok is false and the body is not a recognizable ProblemDetail: a 404 HTML page from a misrouted /login/email-code/send, a 502/504 from Nginx/Cloudflare returning HTML, response.json() parsed something unexpected, or the body is empty. The generic localized 'send verification code failed' message is shown.","commonSituations":"Reverse proxy (Nginx/Traefik) in front of Halo intercepts the request and returns its own HTML error; the endpoint URL changed in a version upgrade but the template still references the old path; response.json() succeeds on a non-error JSON shape (e.g. {status:'error'}) lacking both fields; deployment where login email-code feature/plugin is disabled returns a generic 4xx with an undocumented body.","solutions":["Open DevTools Network and inspect both the status code and the raw response body for /login/email-code/send — a non-JSON/HTML body is the usual cause of this fallback.","Confirm the request URL path matches the backend route registered in the current Halo version (route may have moved under /apis/...).","If behind a proxy, check proxy error pages and ensure /login/* is passed through to the backend rather than served by the proxy.","Add a temporary console.error(response.status, await response.text()) before the cascade to capture the true body during debugging.","Verify the email-code login method is enabled in backend auth configuration; a disabled method can return an undocumented error body."],"exampleFix":"// before\nthrow new Error(i18nResources.sendVerificationCodeFailed);\n// after — preserve status code context for debugging while keeping the localized message\nthrow new Error(`${i18nResources.sendVerificationCodeFailed} (HTTP ${response.status})`);","handlingStrategy":"validation","validationCode":"// Distinguish JSON ProblemDetail from non-JSON (HTML gateway) bodies before the cascade\nasync function parseProblemDetail(response) {\n  const contentType = response.headers.get(\"content-type\") || \"\";\n  if (!contentType.includes(\"application/json\") && !contentType.includes(\"application/problem+json\")) {\n    return { kind: \"non-json\", status: response.status };\n  }\n  let json;\n  try { json = await response.json(); } catch { return { kind: \"invalid-json\", status: response.status }; }\n  if (Array.isArray(json.errors) && json.errors.length) return { kind: \"errors\", json };\n  if (typeof json.detail === \"string\") return { kind: \"detail\", json };\n  return { kind: \"unknown-json\", status: response.status, json };\n}","typeGuard":"function isJsonResponse(response: Response): boolean {\n  const ct = response.headers.get(\"content-type\") || \"\";\n  return ct.includes(\"application/json\") || ct.includes(\"application/problem+json\");\n}","tryCatchPattern":"// Separate the transport failure (non-ok, non-JSON) from the application error\nif (!response.ok) {\n  if (!isJsonResponse(response)) {\n    // gateway/proxy HTML error — this is where fallback [142] fires\n    throw new Error(`${i18nResources.sendVerificationCodeFailed} (HTTP ${response.status})`);\n  }\n  const json = await response.json();\n  // ... handle errors[] / detail as above\n}","preventionTips":["Check response.headers content-type before response.json() to avoid masking HTML errors as 'unexpected token'.","Keep the login template in sync with the backend route path across version upgrades.","Ensure reverse proxies pass /login/* through to Halo instead of serving their own error pages."],"tags":["authentication","fetch","fallback","login","reverse-proxy","i18n"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}