tinyhumansai/openhuman · critical
Core returned an empty backend URL
Error message
Core returned an empty backend URL
What it means
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.
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.
Example fix
// before
const resolved = String(response.api_url ?? response.apiUrl ?? '').trim();
if (!resolved) {
throw new Error('Core returned an empty backend URL');
}
// after — fall back to the web host's own origin in browser dev
const resolved = String(response.api_url ?? response.apiUrl ?? '').trim();
if (!resolved) {
if (import.meta.env.DEV && window.location.origin.includes('localhost')) {
return normalizeBaseUrl(window.location.origin); // dev proxy
}
throw new Error('Core returned an empty backend URL — set BACKEND_API_URL and restart Core');
} Defensive patterns
Strategy: validation
Validate before calling
import { callCoreRpc } from './coreRpcClient';
async function backendUrlConfigured(): Promise<boolean> {
try {
const r = await callCoreRpc<{ api_url?: string; apiUrl?: string }>({
method: 'openhuman.config_resolve_api_url',
});
return Boolean((r.api_url ?? r.apiUrl ?? '').trim());
} catch {
return false;
}
}
if (!await backendUrlConfigured()) {
// show setup UI instead of proceeding to backend calls that will fail Type guard
function hasResolvedApiUrl(res: unknown): res is { api_url?: string; apiUrl?: string } {
const r = res as { api_url?: unknown; apiUrl?: unknown } | null;
return Boolean(r) && (typeof r.api_url === 'string' || typeof r.apiUrl === 'string');
} Try / catch
try {
url = await resolveBackendUrl();
} catch (e) {
if (e instanceof Error && e.message === 'Core returned an empty backend URL') {
openSettings('backend'); // route user to set the backend URL
return;
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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 ''.
Common situations: 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.
Related errors
- oauthAuthReadinessUserMessage(quick.reason)
- OPENAI_CODEX_OAUTH_MISSING_AUTH_URL
- proxy.scope='services' requires a non-empty proxy.services…
- agent ` ` is a `worker` tier and must not list ` ` in its…
- Channel connect response missing status
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/0a60992b3e4a92f5.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/services/backendUrl.ts:63
}
if (!coreIsTauri()) {
resolvedBackendUrl = webFallbackBackendUrl();
return resolvedBackendUrl;
}
if (resolvingBackendUrl) {
return resolvingBackendUrl;
}
const generation = backendUrlGeneration;
resolvingBackendUrl = (async () => {
const response = await callCoreRpc<{ api_url?: string; apiUrl?: string }>({
method: 'openhuman.config_resolve_api_url',
});
const resolved = String(response.api_url ?? response.apiUrl ?? '').trim();
if (!resolved) {
throw new Error('Core returned an empty backend URL');
}
const normalized = normalizeBaseUrl(resolved);
if (generation === backendUrlGeneration) {
resolvedBackendUrl = normalized;
}
return normalized;
})().finally(() => {
if (generation === backendUrlGeneration) {
resolvingBackendUrl = null;
}
});
return resolvingBackendUrl;
}
View on GitHub (pinned to 7491200858)