actualbudget/actual · error
Health check failed: Server responded to health check with s
Error message
Health check failed: Server responded to health check with status ${status} What it means
Thrown by the health-check script when it fetches /health, parses the JSON, and getHealthStatus returns something other than 'UP'. The status (e.g. 'DOWN' or 'DEGRADED') is embedded in the message; the script's catch handler logs and exits non-zero so orchestrators mark the server unhealthy.
Source
Thrown at packages/sync-server/src/scripts/health-check.ts:27
if (
typeof response === 'object' &&
response !== null &&
'status' in response &&
typeof response.status === 'string'
) {
return response.status;
}
return undefined;
}
fetch(`${protocol}://${hostname}:${config.get('port')}/health`)
.then(response => response.json())
.then(response => {
const status = getHealthStatus(response);
if (status !== 'UP') {
throw new Error(
'Health check failed: Server responded to health check with status ' +
status,
);
}
})
.catch(err => {
console.log('Health check failed:', err);
process.exit(1);
});
View on GitHub (pinned to d4334cb6e6)
Solutions
- Read the reported status and server logs to find why /health reports DOWN (usually DB access).
- Verify the account database path/permissions in the sync server config.
- Retry after migrations/startup complete; add startup grace periods to your orchestrator.
- Confirm the script targets the correct host/port where the Actual sync server listens.
Example fix
// before Health check failed: Server responded to health check with status DOWN // after # fix DB path, then yarn workspace @actual-app/sync-server health-check # status UP, exit 0
Defensive patterns
Strategy: retry
Validate before calling
// only run the health check after the server is listening
await waitForPort(config.get('port'), { timeoutMs: 30000 });
// then: const res = await fetch(`http://host:${port}/health`); await res.json(); Type guard
function isHealthCheckFailure(err: unknown): err is Error {
return err instanceof Error && err.message.startsWith('Health check failed:');
} Try / catch
const run = async (retries = 5) => {
for (let i = 0; i < retries; i++) {
try { return await healthCheck(); }
catch (err) {
if (isHealthCheckFailure(err) && i < retries - 1) { await sleep(5000); continue; }
throw err;
}
}
}; Prevention
- Add startup grace periods so probes do not run before the DB/migrations are ready.
- Verify the account DB path and permissions whenever /health reports DOWN.
- Point probes at the configured port, not a default.
- Alert on health-check failures and inspect server logs for the root cause.
When it happens
Trigger: Running the health-check script against a server whose /health reports DOWN — e.g. the account database is unreachable, migrations failed, or the sync server is up but its internal dependencies are broken.
Common situations: Docker/Kubernetes health probes failing during startup before the DB is ready; misconfigured sqlite path making the server report DOWN; using the script against the wrong port or a different service that answers with non-standard health JSON.
Related errors
- No id returned from download.
- response.reason || response.error_code
- response.reason || response.error || fallbackMessage
- Failed to get server config.
- No sync server configured.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/ab3591bcd7aa18dd.
Report an issue: GitHub.