# Debugging Broken Jobs

> Triage entry-point for any "the job ran but something's wrong" situation in a Friday workspace. Loads when a job tool returns `output-error`, `success: false`, `summary: ""`, `artifactIds: []`, or any session with `status: failed`. Routes to the right sibling debug skill. Required reading before declaring "platform bug" / "FSM wiring is broken" / "this is a known issue" — those claims are FORBIDDEN without first walking this triage table.

- Skill: `friday-platform/debugging-broken-jobs` (Agent Skill)
- Install (CLI): `npx skillmds@latest add friday-platform/debugging-broken-jobs`
- Raw SKILL.md: https://api.skillmd.com/api/skills/friday-platform/debugging-broken-jobs/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: friday-platform (https://skillmd.com/u/friday-platform)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/friday-platform/debugging-broken-jobs

---


# Debugging broken jobs

Stop. Before you form a hypothesis, walk this triage. The job tool's
return shape and the session's `agentBlocks[].toolCalls` carry the
diagnosis 95% of the time. The remaining 5% is rarely a platform bug
and almost always a contract violation by the agent or the FSM.

## Diagnostic discipline (non-negotiable)

You may NOT claim "platform bug" / "FSM wiring is broken" / "this is a
known issue" until you have:

1. Called `describe_session(sessionId)` on the failing session.
2. Read `agentBlocks[].toolCalls` to see what tools the agent
   actually invoked.
3. Verified the contracted exit (`complete` for `outputTo` actions,
   terminal text otherwise) was attempted.

If you skipped any of those, you don't know enough to claim a bug.
Run the steps first.

**Run a control before blaming your own change.** When something you just
created or edited fails, fire an equivalent request at something you have
*not* touched this session. If the untouched thing fails the same way, your
edit is not the cause — you're looking at a shared layer (request envelope,
auth, a stale cache, the runtime), so stop re-editing your change. If only
your edited thing fails, then it *is* your edit. Either branch halves the
search space in one call; the expensive mistake is looping on your change on
the assumption it must be the culprit.

## Symptom triage table

Match the job tool's return shape OR the session's status, then load
the matching sibling skill.

| Symptom signature | Load |
|---|---|
| `success: true, status: completed, summary: ""`, `artifactIds: []` | `debugging-empty-output` |
| `success: true, status: completed`, summary mentions "Session completed without producing a summarizable document" | `debugging-empty-output` |
| Session shows many tool calls of the same name, no terminal text, hit `stopWhen: stepCountIs(...)` | `debugging-tool-loops` |
| Session `status: failed`, error contains `LLM action with outputTo '...' did not call complete` | `debugging-runtime-errors` |
| Session `status: failed`, error contains `emitted empty output` or `emitted an empty response` | `debugging-runtime-errors` |
| Session `status: failed`, error contains `LLM step failed: ...` (agent called `failStep`) | `debugging-runtime-errors` |
| Session `status: failed`, error contains `Agent '<id>' output does not match <type> schema` | `debugging-runtime-errors` |
| Session `status: failed`, error contains `Invalid job config` or `Invalid signal config` (Zod rejection) | `debugging-runtime-errors` |
| Job tool returned `output-error` with no meaningful error text (typically called with bare `{}`) | `debugging-job-invocation` |
| Job tool returned `output-error` mentioning "tool isn't bound" — this is wrong; you forgot a `prompt` arg | `debugging-job-invocation` |
| `Model tried to call unavailable tool '<name>'` — you created the job this session; it has no bound tool yet | `debugging-job-invocation` |
| External MCP tool returned 422 / 401 / 403 with provider-side error | check the workspace's MCP server credentials; not a platform bug |

If no row matches, gather more evidence — don't escalate.

## What "diagnostic discipline" looks like in practice

```text
USER: run stale
ASSISTANT [calls workspace job tool]
JOB TOOL: { success: true, status: completed, summary: "", artifactIds: [] }
ASSISTANT [recognizes empty-output signature]
ASSISTANT [load_skill: debugging-empty-output]
ASSISTANT [calls describe_session(sessionId)]
ASSISTANT [reads agentBlocks[0].toolCalls]
ASSISTANT [observes complete was not called; agent emitted text only]
ASSISTANT: The job ran successfully but the agent emitted prose without
calling `complete`. Per the `@friday/agent-action-handshake`
contract, an `outputTo` action requires the agent to call complete
with the final text. Fix the agent's prompt to instruct it explicitly.
```

What you must NOT do:

```text
USER: run stale
ASSISTANT [calls job tool]
JOB TOOL: { summary: "" }
ASSISTANT: This looks like a platform bug. Let me try migrating from
Pattern A to Pattern B FSM…
```

That's a contract violation by you. Load this skill, walk the table,
diagnose properly.

## Cross-references

- `@friday/agent-action-handshake` — the FSM action ↔ agent
  prompt contract that most empty-output failures violate.
- `@friday/writing-workspace-jobs` — FSM authoring once you know
  what to fix.
- `@friday/workspace-api` — agent CRUD once you know the agent
  prompt is the broken part.

