.github/workflows/agent-with-retries.yml
The old shield-masters say: a shield that breaks on first impact is worse than no shield β the warrior trusted it. Build the Shield of Retries with three principles: try again when appropriate, admit defeat when necessary, and always report your final state so the next defender knows where you fell.
πΊοΈ Quest Network Position¶
graph LR
Q6[β
Q6: Bind the Realm] --> Q7[π― Q7: Shield of Retries]
Q7 --> Q8[π Q8: Memory Vaults]
style Q7 fill:#4CAF50,stroke:#2E7D32,stroke-width:4px,color:#fff
π― Quest Objectives¶
- Classify failure types β categorise agent errors into retryable vs. non-retryable
- Implement retry policy β configure exponential backoff in a GitHub Actions agent workflow
- Set timeouts β add per-step and per-job timeouts to prevent infinite loops
- Design an escalation path β agent failure triggers a PR comment + human notification
- Test failure recovery β simulate a tool error and verify the retry + escalation path works
βοΈ The Quest Begins¶
Chapter 1 β Classifying Agent Failures¶
Not all failures are equal. Before building retry logic, classify what can and cannot be retried:
| Failure Type | Example | Retryable? | Strategy |
|---|---|---|---|
| Transient API error | GitHub 503 | β Yes | Exponential backoff, max 3 tries |
| Rate limit | GitHub 429 | β Yes | Wait for Retry-After header |
| Auth error | 401 Unauthorized | β No | Escalate immediately |
| Task ambiguity | Agent cannot determine scope | β No | Stop, report, request clarification |
| Resource not found | 404 on required file | β No | Escalate, do not create wrong file |
| Infinite loop | Agent re-plans endlessly | β No | Hard timeout + escalate |
| Schema validation failure | Plan JSON invalid | β Yes (1x) | Re-plan once, then escalate |
Chapter 2 β Implementing Retry Logic in GitHub Actions¶
Exercise 7.1: Add retry configuration to your agent workflow.
{% raw %}
# .github/workflows/agent-with-retries.yml
name: Agent with Resilient Execution
on:
issues:
types: [labeled]
jobs:
agent-run:
if: contains(github.event.label.name, 'copilot')
runs-on: ubuntu-latest
timeout-minutes: 30 # β Hard job timeout
steps:
- uses: actions/checkout@v4
- name: Attempt agent task with retries
id: agent_task
timeout-minutes: 15 # β Per-step timeout
env:
RETRY_MAX: 3
RETRY_DELAY: 10 # seconds
run: |
for attempt in $(seq 1 $RETRY_MAX); do
echo "=== Attempt $attempt of $RETRY_MAX ==="
if python3 work/gh-600/scripts/run_agent_task.py \
--issue "${{ github.event.issue.number }}" \
--output agent-result.json; then
echo "β
Agent task succeeded on attempt $attempt"
echo "succeeded=true" >> "$GITHUB_OUTPUT"
exit 0
fi
# Check if error is retryable
ERROR_CODE=$(jq -r '.error_code // "unknown"' agent-result.json 2>/dev/null)
if [[ "$ERROR_CODE" == "auth_error" || "$ERROR_CODE" == "scope_ambiguous" ]]; then
echo "β Non-retryable error: $ERROR_CODE β escalating"
echo "succeeded=false" >> "$GITHUB_OUTPUT"
echo "error_code=$ERROR_CODE" >> "$GITHUB_OUTPUT"
exit 1
fi
if [ "$attempt" -lt "$RETRY_MAX" ]; then
DELAY=$((RETRY_DELAY * (2 ** (attempt - 1)))) # exponential backoff
echo "β³ Retryable error. Waiting ${DELAY}s before retry..."
sleep "$DELAY"
fi
done
echo "succeeded=false" >> "$GITHUB_OUTPUT"
echo "error_code=max_retries_exceeded" >> "$GITHUB_OUTPUT"
exit 1
- name: Escalate on failure
if: failure() || steps.agent_task.outputs.succeeded == 'false'
uses: actions/github-script@v7
with:
script: |
const errorCode = '${{ steps.agent_task.outputs.error_code }}' || 'unknown';
const runUrl = `https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## π¨ Agent Task Failed β Human Intervention Required\n\n` +
`**Error code:** \`${errorCode}\`\n` +
`**Run:** ${runUrl}\n\n` +
`The agent could not complete this task. Please review the run logs ` +
`and either clarify the task or complete it manually.\n\n` +
`To retry with a clearer task description, close and reopen this issue ` +
`with an updated body.`
});
Chapter 3 β Designing the Escalation Ladder¶
An escalation ladder defines what happens at each failure level:
Level 1 β Retryable error: Retry with backoff (up to max_retries)
Level 2 β Max retries hit: Comment on PR/issue + add "needs-human" label
Level 3 β Non-retryable error: Immediate comment + alert + stop execution
Level 4 β Timeout: Hard stop + comment + disable agent label
Exercise 7.2: Add "needs-human" label creation to your escalation step.
- name: Add needs-human label on escalation
if: failure()
uses: actions/github-script@v7
with:
script: |
// Ensure label exists
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'needs-human',
color: 'FF4444',
description: 'Agent requires human intervention'
});
} catch (e) { /* label already exists */ }
// Apply label to issue
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['needs-human']
});
Chapter 4 β Testing Failure Scenarios¶
Exercise 7.3: Create a test that deliberately causes each failure type and verify the escalation path.
#!/usr/bin/env bash
# work/gh-600/scripts/test_failure_scenarios.sh
set -euo pipefail
echo "=== Testing Agent Failure Scenarios ==="
# Scenario 1: Transient error β should retry
echo "Test 1: Transient error (expect retry)"
# Mock a 503 response and verify the workflow retries 3 times
# Scenario 2: Auth error β should escalate immediately
echo "Test 2: Auth error (expect immediate escalation, no retry)"
# Mock a 401 response and verify workflow does NOT retry
# Scenario 3: Timeout β should escalate with timeout error code
echo "Test 3: Step timeout (expect hard stop + escalation)"
# Run a step that sleeps > timeout_minutes
echo "=== All failure scenarios validated ==="
Chapter 5 β Agent Error Reporting Format¶
Every failed agent run should produce a machine-readable error report:
{
"error_report_version": "1.0",
"run_id": "github-run-id",
"error_code": "max_retries_exceeded",
"error_category": "retryable",
"attempts": 3,
"last_error_message": "GitHub API returned 503: Service Unavailable",
"recommended_action": "Retry with same task after GitHub status is green",
"run_url": "https://github.com/org/repo/actions/runs/ID",
"escalated": true,
"escalated_at": "2026-05-17T10:30:00Z"
}
β Quest Validation¶
Run this self-contained check (no external script needed) from your sandbox repo root:
WF=.github/workflows/agent-with-retries.yml
test -f "$WF" && echo "β
Workflow: agent-with-retries.yml present" || echo "β Workflow missing"
grep -q 'RETRY_MAX' "$WF" && echo "β
Retry config: exponential backoff implemented" || echo "β No retry config"
grep -q 'timeout-minutes' "$WF" && echo "β
Timeout: job-level and step-level timeouts set" || echo "β No timeouts"
grep -q 'createComment' "$WF" && echo "β
Escalation: PR comment + label on failure" || echo "β No escalation"
grep -q 'error_report_version' work/gh-600/scripts/*.json 2>/dev/null && echo "β
Error report schema: present" || echo "βΉοΈ Error report schema: sample only (see Chapter 5)"
# π All green β Quest Q7 complete!
π Quest Rewards¶
| Reward | Details |
|---|---|
| π‘οΈ Resilience Keeper Badge | Earned on completion |
| β»οΈ Agent Retry Patterns | Skill unlocked |
| 100 XP | Added to Level 1001 total |
| Unlocks | Q8: Vaults of Recollection |
πΈοΈ Knowledge Graph¶
Structured wiki-links connect this quest to the IT-Journey knowledge graph. Open the Obsidian Graph View to explore connections.
Level hub: [[Level 1001 (9) - Kubernetes Orchestration]] Overworld: [[π° Overworld - Master Quest Map]] Study track: [[The Agentic Codex: GH-600 Study Hub]] Β· [[GH-600 Agentic AI Quick-Reference Notes]] Prerequisites: [[Bind the Agent to the Realm: Dev Environment Integration]] Unlocks: [[Vaults of Recollection: Agent Memory Strategies]] Sequel quests: [[Vaults of Recollection: Agent Memory Strategies]] Obsidian docs: [[Obsidian Knowledge Graph and Wiki Links]]