{"record":{"id":"ad09c2a4e520d712","repo":"halo-dev/halo","slug":"json-detail-ad09c2","errorCode":null,"errorMessage":"json.detail","messagePattern":"json\\.detail","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"application/src/main/resources/templates/login_email-code.html","lineNumber":71,"sourceCode":"          throw new Error(/*[[#{form.emailCode.send.emptyValidation}]]*/ \"\");\n        }\n\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":53,"sourceCodeEnd":84,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/resources/templates/login_email-code.html#L53-L84","documentation":"Sibling branch in the same email-code login error cascade. The backend returned a non-2xx ProblemDetail body that has a `detail` string but no `errors` array. Spring populates `detail` for generic problem failures (e.g. rate-limited, email service unavailable, internal error) where there is no single field to blame. The detail text is re-thrown as the Error message.","triggerScenarios":"The /login/email-code/send call fails with a ProblemDetail whose `errors` array is absent/empty but `detail` is set. This happens on: email-code send rate limiting (too many codes requested), the SMTP/notifier backend being down or misconfigured, the email address not matching a known user when enumeration protection is disabled, or a 500 from the notification service.","commonSituations":"SMTP/mail sender not configured in application.yaml (spring.mail.* missing) so the backend returns a problem detail; hitting the per-session/per-email rate limit on code sending; a plugin implementing the verification-code extension throws a business exception that Spring maps to a ProblemDetail with detail only; network blip causing a gateway 5xx that Spring Boot renders with a detail field.","solutions":["Read json.detail from the Network tab response — it names the actual cause (e.g. 'Too many requests', 'Mail service not available').","Check the backend notification/email configuration (application.yaml spring.mail.host, the notifier plugin) if detail mentions sending failure.","If detail indicates rate limiting, throttle the send button via sendVerificationCode's countdown and respect Retry-After.","Ensure the backend email-code endpoint is reachable and not returning 404 (path misconfiguration) which some proxies render as a detail-only body.","Tail backend logs around the timestamp of the failing request for the underlying exception."],"exampleFix":"// before\nif (json.detail) {\n  throw new Error(json.detail);\n}\n// after — guard for non-string detail and prefer detail over a raw object dump\nif (typeof json.detail === \"string\" && json.detail.trim()) {\n  throw new Error(json.detail);\n}","handlingStrategy":"try-catch","validationCode":"// Pre-flight checks that avoid common detail-only failures\nfunction canSendEmailCode(lastSentAt) {\n  // Respect client-side rate limiting to avoid 'Too many requests' detail\n  const MIN_INTERVAL_MS = 60_000;\n  return !lastSentAt || Date.now() - lastSentAt > MIN_INTERVAL_MS;\n}","typeGuard":"// RFC 7807 ProblemDetail with a string detail and no errors array\nfunction hasProblemDetail(body: unknown): body is { detail: string } {\n  return (\n    typeof body === \"object\" && body !== null &&\n    typeof (body as any).detail === \"string\" &&\n    (!Array.isArray((body as any).errors) || (body as any).errors.length === 0)\n  );\n}","tryCatchPattern":"try {\n  const response = await sendRequest();\n  // success path\n} catch (e) {\n  // e.message is the server's json.detail — map known details to localized copy\n  const msg = e instanceof Error ? e.message : \"\";\n  if (/too many/i.test(msg)) showRateLimitNotice();\n  else showError(msg || i18nResources.sendVerificationCodeFailed);\n}","preventionTips":["Throttle the 'Send Code' button (60s countdown) to avoid rate-limit detail responses.","Confirm the SMTP/notifier backend is configured before enabling email-code login.","Cache the last error detail to avoid spamming the server on repeated clicks."],"tags":["authentication","rfc7807","email-verification","rate-limiting","smtp","fetch"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}