{"record":{"id":"0a60992b3e4a92f5","repo":"tinyhumansai/openhuman","slug":"core-returned-an-empty-backend-url","errorCode":null,"errorMessage":"Core returned an empty backend URL","messagePattern":"Core returned an empty backend URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"app/src/services/backendUrl.ts","lineNumber":63,"sourceCode":"  }\n\n  if (!coreIsTauri()) {\n    resolvedBackendUrl = webFallbackBackendUrl();\n    return resolvedBackendUrl;\n  }\n\n  if (resolvingBackendUrl) {\n    return resolvingBackendUrl;\n  }\n\n  const generation = backendUrlGeneration;\n  resolvingBackendUrl = (async () => {\n    const response = await callCoreRpc<{ api_url?: string; apiUrl?: string }>({\n      method: 'openhuman.config_resolve_api_url',\n    });\n    const resolved = String(response.api_url ?? response.apiUrl ?? '').trim();\n    if (!resolved) {\n      throw new Error('Core returned an empty backend URL');\n    }\n    const normalized = normalizeBaseUrl(resolved);\n    if (generation === backendUrlGeneration) {\n      resolvedBackendUrl = normalized;\n    }\n    return normalized;\n  })().finally(() => {\n    if (generation === backendUrlGeneration) {\n      resolvingBackendUrl = null;\n    }\n  });\n\n  return resolvingBackendUrl;\n}\n","sourceCodeStart":45,"sourceCodeEnd":78,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/services/backendUrl.ts#L45-L78","documentation":"Thrown by resolveBackendUrl() when the core RPC 'openhuman.config_resolve_api_url' resolves but yields an empty string in both snake_case (api_url) and camelCase (apiUrl) after trimming. The core derives the backend URL from its config (env/TOML); an empty resolution means the core has no backend URL configured at all, so every subsequent backend API call would target ''. Fails fast here instead of issuing requests to a relative/invalid origin.","triggerScenarios":"Fresh core config with BACKEND_API_URL / backend api_url unset and no default applying; config file missing or unreadable so the field stays empty; the RPC returned an error object (no api_url keys) that String() coerced to ''.","commonSituations":"Dev setup without sourcing scripts/load-dotenv.sh or copying .env.example; a wiped ~/.openhuman config; running a standalone core whose TOML lacks the backend section; pointing at a core built with a restricted DomainSet that omitted config resolution.","solutions":["Set the backend URL for the core: BACKEND_API_URL in .env (then source scripts/load-dotenv.sh and restart) or the backend URL field in Settings.","Restart the core after config changes so config_resolve_api_url re-reads them.","Check the core log at boot for config-load errors (missing/unreadable TOML) that leave the field empty.","If the RPC response is an error envelope rather than a payload, fix the underlying RPC failure first — log the raw response."],"exampleFix":"// before\nconst resolved = String(response.api_url ?? response.apiUrl ?? '').trim();\nif (!resolved) {\n  throw new Error('Core returned an empty backend URL');\n}\n\n// after — fall back to the web host's own origin in browser dev\nconst resolved = String(response.api_url ?? response.apiUrl ?? '').trim();\nif (!resolved) {\n  if (import.meta.env.DEV && window.location.origin.includes('localhost')) {\n    return normalizeBaseUrl(window.location.origin); // dev proxy\n  }\n  throw new Error('Core returned an empty backend URL — set BACKEND_API_URL and restart Core');\n}","handlingStrategy":"validation","validationCode":"import { callCoreRpc } from './coreRpcClient';\n\nasync function backendUrlConfigured(): Promise<boolean> {\n  try {\n    const r = await callCoreRpc<{ api_url?: string; apiUrl?: string }>({\n      method: 'openhuman.config_resolve_api_url',\n    });\n    return Boolean((r.api_url ?? r.apiUrl ?? '').trim());\n  } catch {\n    return false;\n  }\n}\nif (!await backendUrlConfigured()) {\n  // show setup UI instead of proceeding to backend calls that will fail","typeGuard":"function hasResolvedApiUrl(res: unknown): res is { api_url?: string; apiUrl?: string } {\n  const r = res as { api_url?: unknown; apiUrl?: unknown } | null;\n  return Boolean(r) && (typeof r.api_url === 'string' || typeof r.apiUrl === 'string');\n}","tryCatchPattern":"try {\n  url = await resolveBackendUrl();\n} catch (e) {\n  if (e instanceof Error && e.message === 'Core returned an empty backend URL') {\n    openSettings('backend'); // route user to set the backend URL\n    return;\n  }\n  throw e;\n}","preventionTips":["Source scripts/load-dotenv.sh and set BACKEND_API_URL before first run.","Fail fast at app boot with a setup screen when the resolution is empty.","Restart the core after changing backend URL config so the RPC re-reads it."],"tags":["config","backend-url","core-rpc","environment","startup"],"backgroundTag":"missing-backend-url-config","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}