Skip to content

Debugging an agent

When the agent isn't doing what you expect, here's the order to look. Most issues fall into a small set of patterns; this page is the field guide.

The first three checks

Before anything else, check these in the agent's detail page header:

  1. Status pill. If it says paused, that's why. Click Resume.
  2. Heartbeat indicator. If it's off, the agent only runs when manually triggered. Configuration tab → flip it on.
  3. Latest run. Configuration tab → Runs. If the latest row is failed, click in to see the error. If the latest row is running and stale (started >5 min ago), the worker pool may be stuck — see "Worker pool stuck" below.

"The agent did nothing"

The agent appears to ignore an issue.

Likely causes

Symptom Cause Fix
Heartbeat off heartbeat_enabled: false Configuration tab → enable.
Heartbeat very long interval e.g., 600s Lower it, or Run Heartbeat to fire one now.
Issue not assigned to this agent Agent only sees its own assigned issues + wakeups Set assignee on the issue, or @mention the agent.
Issue status done/cancelled Agent skips closed issues Move back to todo / in_progress.
Agent is paused Status badge says paused Click Resume.
Agent is checked out on a different issue Soft lock per agent The other run will release at end of turn — wait.
Provider key is wrong Adapter call fails fast Configuration tab → Test Adapter. Fix the key.
Budget incident halt New heartbeats blocked Costs → Incidents → Resolve (admin).

How to investigate

  1. Runs tab. Look at the last few runs. Failed runs will tell you what went wrong.
  2. Test Adapter button. Top of the agent page. Confirms the provider key + endpoint work.
  3. Trigger Heartbeat button. Force one tick. If it doesn't run, the issue is upstream of the heartbeat scheduling.
  4. Activity panel for this agent. Records pauses, role changes, provider changes that might explain a sudden behaviour shift.

"The agent ran but the answer is bad"

The agent picked up the issue, replied, but the reply is hallucinated, off-topic, or violates the rules.

Likely causes

  • System prompt is unclear or contradictory. Re-read it as if you were the agent. Look for ambiguity or conflicting rules.
  • Issue context is too thin. The agent has nothing to act on. Add detail to the issue body.
  • Role is wrong for the work. A pm-roled agent will coordinate; it won't write code. Reassign to an engineer or change the role.
  • Model is wrong for the work. Cheap fast models miss edge cases. Bump to a stronger model on the Configuration tab.
  • The skill isn't enabled. A DB skill only reaches agents once it's enabled — it then inlines into the ## Available Skills section of every heartbeat prompt for the company. If the procedure you assumed the agent had isn't enabled, it was never in context. (Filesystem skills reach only CLI-backed adapters, via the workspace dir — see writing-a-skill.md.)
  • Skill description is buried. The "use this skill when X" line must be in the first paragraph of the SKILL.md, not paragraph six — the body is inlined whole, but the model decides relevance from the top.

How to investigate

  • Runs tab → click into a run. You see the full prompt the agent received and the full reply. The prompt is the truth — if the expected instructions aren't in there, the agent never had them.
  • Diff against the role default. GET /api/roles/<role>/default-prompt returns the role's untouched prompt; compare against the agent's stored prompt to see what was edited.
  • Comment on the issue with feedback. Then Run Agent again. Agents self-correct on subsequent heartbeats given new context. If the agent doesn't correct after explicit feedback, the prompt or model is the cause.

"The agent is bouncing tickets"

The triage agent → assigns to engineer → who reassigns back → who escalates → who reassigns again. Looks like a loop.

Likely causes

  • Agent capabilities don't match the work. The triage agent routed to an LLM-typed agent for a task that requires claude_local (filesystem), or vice versa. The accepting agent bounces it because it can't do the work.
  • Missing assignee. The triage agent's escalation playbook reassigns to a manager that doesn't exist on this team.
  • Org chart is broken. Multiple agents have the same display name, or reports_to points at a deleted agent.

How to investigate

  • Org chart page. /companies/<id>/org-chart. Look for orphans, duplicate names, broken parent links.
  • Issue Timeline. Each bounce leaves a status_change and issue.updated (assignee) entry. Read the agents' comments to see why they're rejecting.
  • The escalation playbook is in every prompt. If you don't want agents to bounce, edit their prompt to override the playbook.

"The agent's filesystem write didn't land (where I expected)"

/write-file was emitted but the file isn't where you looked. Remember that /write-file does not drop when the issue has no project — it falls through a four-tier cascade (project → company files → instance files → issue comment). So "the file is missing" is usually "the file landed in a fallback location" or "the verb was malformed". See the-write-file-action.md.

Likely causes (in order of frequency)

  1. Issue has no project, so it fell back. Look in the company files dir (<company-home>/files/) or instance files dir ($TRIMUS_HOME/files/) — the engine logged falling back to company root / falling back to instance root and recorded a company.file_written / instance.file_written activity. Attach the issue to a project to keep writes in the project workspace.
  2. Body fence missing or unterminated. The verb line stays as prose and no file is written — the single most common real miss.
  3. Path validation failed. Absolute path, .. traversal, null byte, or a path that resolves outside the workspace. The write is skipped and the rejection is logged to stderr.
  4. File too large (>4 MiB: content exceeds 4194304 byte limit).
  5. All filesystem tiers failed (permissions/disk). The body was preserved as a collapsible issue comment with a file.written_as_comment activity — check the timeline. Fix the directory permissions: the OS user running trimus-server needs write access to the resolved workspace dir.

How to investigate

  • Server logs. Every tier and rejection logs to stderr. journalctl -u trimus (or wherever your server logs go) and grep for write_file.
  • Issue Timeline. A successful write produces a *.file_written activity entry naming the tier; absence (and a file.written_as_comment entry) tells you the cascade fell all the way through.
  • Run transcript. Confirms whether the agent emitted the verb in the right format (path on the verb line, body in the next fence).

"The agent loops"

The agent keeps running on the same issue, posting similar comments, not closing it.

Likely causes

  • No exit condition in the prompt. "Keep working until done" with no definition of done.
  • Approval requested but never resolved. The agent is waiting; it re-tries on each heartbeat. Resolve the approval.
  • Heartbeat is too short for the work. The agent times out mid- thought. Bump the interval.
  • Status never set to done or in_review. The agent doesn't know it can stop.

How to investigate

  • Runs tab. Are runs producing useful output but not closing the issue? Prompt issue. Are runs timing out? Interval issue.
  • Approvals → Pending. Check if this agent has open requests.

"The whole worker pool is stuck"

No agents running, dashboard shows everyone idle, no Runs starting.

Likely causes

  • Database connection pool exhausted. Check /api/health.
  • Worker pool crashed. Restart trimus-server.
  • A long-running heartbeat is hogging the only worker slot. Check for running heartbeat runs >10 min old in any agent's Runs tab — usually a stuck claude_local subprocess.

How to investigate

  • /api/health — should return 200. If it doesn't, postgres or the connection pool is the problem.
  • SELECT * FROM heartbeat_runs WHERE status='running' ORDER BY started_at in postgres — anything stuck >5 min is suspicious.
  • Restart trimus-server is safe — it doesn't lose state. Stuck runs will be marked failed and the queue drains.

"I changed the prompt but the agent still acts the old way"

  • Confirm save. Configuration tab → System Prompt → did you click Save Configuration?
  • Run a fresh heartbeat after save. The old run's transcript still shows the old prompt.
  • Editing internal/roles/data/<role>.md does not auto-apply to existing agents. See system-prompts.md.
  • Claude session persistence — if the adapter is claude_local, the session may be holding state from before. Click Clear Session in the agent header.

When all else fails

  • Pause the agent. The world stops moving for it; you can think.
  • Read the last successful Run's transcript. Working state is the best baseline.
  • Compare the last successful run's prompt to the current prompt. Diffs reveal recent edits that broke things.
  • Reset to the role default prompt by deleting your override (set system_prompt to empty via PATCH /api/agents/{id}).
  • If the agent is fundamentally not the right fit, terminate and re-hire with a different adapter / role / model.

Where to next