Reading run error logs in the dashboard, replaying a failed run via the API, using the --dry-run flag, and a reference table of common failure signatures per adapter type.
Systematic approach to diagnosing heartbeat failures, reading error details from the database, and reproducing adapter errors locally.
When a heartbeat run fails or an issue stays stuck in 'open':
1. Check heartbeat_runs.status and error_detail
2. Check issues.attempt_count vs max_attempts
3. Check agent status (active / paused)
4. Check company paused flag
5. Check API key environment variables
6. Reproduce the adapter call locally
7. Check adapter logs (for shell/http adapters)
SELECT
id,
status,
error_detail,
skip_reason,
started_at,
finished_at
FROM heartbeat_runs
WHERE company_id = 'your-company-id'
AND status IN ('failed', 'skipped')
ORDER BY started_at DESC
LIMIT 5;
error_detail is a JSONB array — each element represents one agent failure:
[
{
"agent_id": "...",
"agent_name": "Researcher",
"issue_id": "...",
"error": "TimeoutError: adapter invoke exceeded 60000ms",
"attempt": 2
}
]
SELECT
id, title, status, attempt_count, max_attempts
FROM issues
WHERE company_id = 'your-company-id'
AND status IN ('open', 'escalated')
ORDER BY attempt_count DESC;
Issues with attempt_count = max_attempts and status = 'escalated' have given up and need human review.
For claude-code:
# Get the system prompt and a failing issue's description
SYSTEM=$(psql $DATABASE_URL -t -c "SELECT system_prompt FROM agents WHERE name='Researcher' LIMIT 1")
TASK=$(psql $DATABASE_URL -t -c "SELECT description FROM issues WHERE id='failing-issue-id'")
# Invoke directly
echo "${SYSTEM}\n\n${TASK}" | claude --model claude-opus-4-5 --output-format json
For http adapter:
curl -X POST http://your-endpoint/api/generate \
-H "Content-Type: application/json" \
-d '{"prompt": "test prompt", "model": "llama3"}'
In development, the API server logs each heartbeat tick:
[HEARTBEAT] company=acme tick=started
[HEARTBEAT] agent=Researcher issue=pcl-001 status=claimed
[HEARTBEAT] agent=Researcher adapter=invoke started
[ADAPTER:claude-code] spawn pid=12345
[ADAPTER:claude-code] exit code=1 stderr="ENOENT: claude not found"
[HEARTBEAT] agent=Researcher issue=pcl-001 status=failed attempt=1/3
[HEARTBEAT] company=acme tick=completed agents=1 resolved=0 failed=1
| Error | Fix |
|---|---|
ENOENT: claude not found |
npm install -g @anthropic-ai/claude-code |
401 Unauthorized |
Verify ANTHROPIC_API_KEY in .env |
429 Too Many Requests |
Reduce max_concurrent_agents |
JSON parse error |
Check adapter --output-format json flag |
company_paused (skip) |
Check monthly budget; reset: UPDATE companies SET paused=false |
no_open_issues (skip) |
Seed issues or verify agent assignment |
heartbeat_runs.error_detail and interpret the JSONclaude-code adapter call in the terminal