# How to Handle Error Responses in Claude Plugin Skills: A Complete Guide

> Master handling Claude plugin skill errors. Inspect tool responses and report status using a three-step pattern: detect JSON-RPC errors, verify rows, and poll for DONE or ERROR.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-09-01

---

**Handle Claude plugin skill errors by inspecting both the immediate tool-level response and the downstream report status, using a three-step pattern: detect JSON-RPC errors, verify the report row exists, and poll until DONE or ERROR.**

Claude plugin skills communicate with back-end services through the `execute` tool, typically targeting the **tres-mcp** GraphQL BFF. Because this backend returns errors across multiple channels, robust error handling requires checking every layer of the response. This guide explains the complete pattern, drawn from the `anthropics/claude-plugins-community` source code.

## The Two-Layer Error Model

The tres-mcp backend signals failures through three distinct channels. Your skill must check all of them:

| Error Channel | Source Location | Meaning |
|-------------|-----------------|---------|
| **HTTP 400** with `error` or `error_type` fields | [`run_report_matrix.py`](https://github.com/anthropics/claude-plugins-community/blob/main/run_report_matrix.py) lines 147-148 | Malformed request (wrong types, missing filters). No report created. |
| **GraphQL `errors` array** | [`run_report_matrix.py`](https://github.com/anthropics/claude-plugins-community/blob/main/run_report_matrix.py) line 165 | Query rejected at GraphQL layer (syntax errors, unknown fields). No data returned. |
| **Report `status: ERROR`** | [`run_report_matrix.py`](https://github.com/anthropics/claude-plugins-community/blob/main/run_report_matrix.py) lines 340-341 | Generator ran but failed to complete. Report exists but is unusable. |

Missing any of these checks leads to silent failures or false positives.

## Step 1: Detect Immediate Tool-Level Errors

The first checkpoint unwraps the raw tool result. In [`tres-finance-plugin/skills/tres-report-create/tests/run_report_matrix.py`](https://github.com/anthropics/claude-plugins-community/blob/main/tres-finance-plugin/skills/tres-report-create/tests/run_report_matrix.py) (lines 147-165), the skill inspects for both JSON-RPC errors and GraphQL-level errors:

```python
def trigger_report(vars: dict) -> dict:
    result = execute_tool("mcp", "execute", vars)   # tool call

    
    if "error" in result:
        raise RuntimeError(f"Trigger error: {result['error']}")
    
    if "errors" in result:
        return {"errors": result["errors"]}          # GraphQL-level errors

    
    return result

```

**Key distinction**: The `"error"` field indicates a JSON-RPC layer failure, while `"errors"` contains GraphQL validation problems. Handle both separately.

## Step 2: Verify the Report Row Exists (Silent-Failure Guard)

A dangerous edge case: the BFF may return HTTP 200 with no `error` field yet still fail to create a report row. According to [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) §Step 1c (lines 108-130), you must query the `report` table by exact `exportName` to confirm creation:

```python
def verify_row(export_name: str) -> dict:
    query = """
    query($name: String, $ordering: String, $limit: Int) {
      report(name: $name, ordering: $ordering, limit: $limit) {
        results { id name status }
      }
    }"""
    variables = {
        "name": export_name,
        "ordering": "-created_at",
        "limit": 1
    }
    resp = execute_tool("mcp", "query", {
        "query": query,
        "variables": variables
    })
    
    if not resp["data"]["report"]["results"]:
        raise RuntimeError("Silent failure: no report row created")
    
    return resp["data"]["report"]["results"][0]

```

If verification fails, retry once after fixing variable naming or types, then abort with a clear user message.

## Step 3: Poll Until Final Status

Reports start with status `IN_PROGRESS`. Per [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) §Step 2 (lines 56-62), poll until the report reaches terminal state:

```python
def poll_report(export_name: str, max_attempts: int = 10):
    for attempt in range(max_attempts):
        row = verify_row(export_name)
        status = row["status"]
        
        if status == "DONE":
            return row["link"]
        
        if status == "ERROR":
            raise RuntimeError(
                f"Report error: {row.get('errorMessage', 'unknown')}"
            )
        
        time.sleep(60)   # wait before next poll

    
    raise TimeoutError("Report did not finish in time")

```

When status is `ERROR`, consult the error-to-fix mapping in [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) lines 94-102 to adjust input parameters.

## Critical Error Prevention Practices

The [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) documentation (lines 103-112) specifies strict requirements that prevent common error conditions:

- **Strict variable typing** — Declare variables with exact GraphQL types: `String`, `ReportOutputFormat`, `DateTime`, `[String]`. Type mismatches trigger HTTP 400 errors.

- **Exact naming** — Use `exportName`, `exportFormat`, `currency`, `outputFormat`, `timestamp_Gte`, `timestamp_Lte` exactly. Inline literals or renamed variables cause silent failures.

- **Unique `exportName` per run** — Ensures the verification query matches the newly created row (line 129).

- **Graceful fallback** — When `IN_PROGRESS` exceeds retry limits, report timeout and suggest alternatives.

- **Markdown-formatted links** — Present presigned download URLs as markdown rather than raw text (line 67).

## Key Files for Error Handling Reference

| File | Purpose |
|------|---------|
| [`tres-finance-plugin/skills/tres-report-create/tests/run_report_matrix.py`](https://github.com/anthropics/claude-plugins-community/blob/main/tres-finance-plugin/skills/tres-report-create/tests/run_report_matrix.py) | Core error-unwrapping and polling logic (lines 147-165, 340-341) |
| [`tres-finance-plugin/skills/tres-report-create/SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/tres-finance-plugin/skills/tres-report-create/SKILL.md) | Full workflow, error-to-fix table, variable requirements |
| [`tres-finance-plugin/skills/tres-report-analyzer/scripts/analyze_report.py`](https://github.com/anthropics/claude-plugins-community/blob/main/tres-finance-plugin/skills/tres-report-analyzer/scripts/analyze_report.py) | Downstream analysis of successful downloads |
| [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json) | Skill entry-point and `execute` tool definition |

## Summary

- **Check two error channels**: Inspect `result["error"]` for JSON-RPC failures and `result["errors"]` for GraphQL problems immediately after tool execution.

- **Prevent silent failures**: Query the `report` table by exact `exportName` to confirm row creation before assuming success.

- **Poll to completion**: Monitor `status` field until `DONE` (return link) or `ERROR` (surface message and apply fix).

- **Enforce strict contracts**: Use exact variable names and GraphQL types to avoid HTTP 400 and silent failure modes.

## Frequently Asked Questions

### What causes HTTP 400 errors in Claude plugin skills?

HTTP 400 errors occur when request variables have incorrect types or missing required filters. The `error` or `error_type` fields in the tool result indicate these validation failures. Fix by matching exact GraphQL types specified in [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) lines 103-112.

### How do I detect silent failures where no report is created?

Query the `report` table using the exact `exportName` as shown in [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) §Step 1c. If `results` is empty despite no tool-level error, a silent failure occurred. Retry once after correcting variable names or types.

### What should I do when a report status remains IN_PROGRESS too long?

Implement a maximum attempt limit (recommended: 10 attempts with 60-second delays). If exceeded, raise `TimeoutError` and report to the user with alternative report suggestions. Never leave users waiting indefinitely.

### How do I handle the ERROR status in a report row?

Extract the `errorMessage` field and surface it to the user. Then consult the error-to-fix mapping in [`SKILL.md`](https://github.com/anthropics/claude-plugins-community/blob/main/SKILL.md) lines 94-102 to determine which input parameters need adjustment before retrying.