{"record":{"id":"b8c5a1c316d144aa","repo":"koala73/worldmonitor","slug":"convex-embed-key-validation-unavailable-http-resp-status","errorCode":null,"errorMessage":"Convex embed key validation unavailable: http-${resp.status}","messagePattern":"Convex embed key validation unavailable: http-(.+?)","errorType":"exception","errorClass":"EmbedKeyUnavailableError","httpStatus":null,"severity":"error","filePath":"server/_shared/embed-key.ts","lineNumber":159,"sourceCode":"\n  let resp: Response;\n  try {\n    resp = await fetch(`${convexSiteUrl}/api/internal-validate-embed-key`, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'User-Agent': 'worldmonitor-gateway/1.0',\n        'x-convex-shared-secret': convexSharedSecret,\n      },\n      body: JSON.stringify({ keyHash }),\n      signal: AbortSignal.timeout(3_000),\n    });\n  } catch {\n    throw new EmbedKeyUnavailableError('Convex embed key validation unavailable: fetch-error');\n  }\n\n  if (!resp.ok) {\n    throw new EmbedKeyUnavailableError(\n      `Convex embed key validation unavailable: http-${resp.status}`,\n    );\n  }\n\n  let value: unknown;\n  try {\n    value = await resp.json();\n  } catch {\n    throw new EmbedKeyUnavailableError('Convex embed key validation unavailable: invalid-json');\n  }\n\n  if (value === null) return null;\n  if (!isEmbedKeyResult(value)) {\n    throw new EmbedKeyUnavailableError('Convex embed key validation unavailable: invalid-payload');\n  }\n  return value;\n}\n","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/server/_shared/embed-key.ts#L141-L177","documentation":"After fetch succeeds, fetchFromConvex checks `resp.ok` and throws EmbedKeyUnavailableError with `http-<status>` when the Convex internal endpoint returns a non-2xx status. Like the other variants, this means validation is unavailable — it is not a statement that the submitted embed key is wrong.","triggerScenarios":"The Convex endpoint responds with any non-ok status: 401/403 when the `x-convex-shared-secret` header doesn't match the deployment's secret, 404 when internal-validate-embed-key isn't deployed, 5xx on a Convex function error.","commonSituations":"CONVEX_SERVER_SHARED_SECRET rotated on one side only (worker vs Convex deployment); internal endpoint not yet deployed or removed in a Convex push; Convex function throwing due to a bad request shape or internal failure; a proxy returning 502/503.","solutions":["Read the exact status from the error message; for 401/403, re-sync CONVEX_SERVER_SHARED_SECRET with the Convex deployment's configured shared secret and redeploy both sides.","For 404, confirm the internal-validate-embed-key endpoint exists in the deployed Convex functions and deploy the latest Convex code.","For 5xx, check Convex dashboard logs for the failing function invocation and fix the function error.","Retry after resolving — transient 5xx/502/503 from the platform usually clears once the underlying cause is fixed."],"exampleFix":"// before: mismatched secrets -> http-401\n// worker: CONVEX_SERVER_SHARED_SECRET=old-secret\n// convex: secret = \"new-secret\"\n\n// after: same secret on both sides\n// worker: CONVEX_SERVER_SHARED_SECRET=new-secret\n// convex: secret = \"new-secret\"","handlingStrategy":"try-catch","validationCode":"const probe = await fetch(`${process.env.CONVEX_SITE_URL}/api/internal-validate-embed-key`, {\n  method: 'POST',\n  headers: { 'x-convex-shared-secret': process.env.CONVEX_SERVER_SHARED_SECRET ?? '' },\n  body: JSON.stringify({ keyHash: 'probe' }),\n});\nif (probe.status === 401 || probe.status === 403) throw new Error('Shared secret out of sync with Convex deployment');\nif (probe.status === 404) throw new Error('internal-validate-embed-key endpoint missing from Convex deployment');","typeGuard":null,"tryCatchPattern":"try {\n  const verdict = await result(keyHash);\n} catch (err) {\n  if (err instanceof EmbedKeyUnavailableError && /http-\\d+$/.test(err.message)) {\n    const status = err.message.match(/http-(\\d+)$/)?.[1];\n    console.error(`Convex validator returned HTTP ${status}; failing closed until config/deploy is fixed`);\n    return failClosed();\n  }\n  throw err;\n}","preventionTips":["Rotate CONVEX_SERVER_SHARED_SECRET simultaneously in the Convex deployment and every consumer to avoid 401/403.","Verify the internal endpoint is deployed (no 404) before shipping workers that depend on it.","Monitor Convex function logs so 5xx responses are caught before embed-key validation traffic hits them.","Alert on http-4xx/5xx variants of EmbedKeyUnavailableError — each status points at a distinct, fixable cause."],"tags":["http","convex","embed-key","api"],"backgroundTag":"http-error-response","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}