{
  "schema": "shadow.public_failure_case_index.v1",
  "generated_at": "2026-07-06T08:12:36.742345+00:00",
  "count": 1,
  "cases": [
    {
      "schema": "shadow.public_failure_artifact.v1",
      "id": "graphinterrupt-not-re-raised-in-awrap-tool-call-wrap-292e4a05d8",
      "ts": "2026-07-05T12:12:18.801927+00:00",
      "title": "GraphInterrupt Not Re-raised in awrap_tool_call Wrapper Path",
      "source": {
        "channel": "unsolicited_triage",
        "repo": "langchain-ai/langgraph",
        "issue_number": 8217,
        "author": "shivajith",
        "url": "https://github.com/langchain-ai/langgraph/issues/8217"
      },
      "case": {
        "observed_action": "ToolNode's awrap_tool_call wrapper path catches GraphInterrupt in a generic except Exception block and passes it to handle_tool_errors, producing a ToolMessage with error content instead of suspending the graph.",
        "expected_boundary": "GraphInterrupt (a GraphBubbleUp subclass) must be re-raised untouched at any exception boundary inside tool execution, including inside awrap_tool_call middleware, so the graph suspends instead of treating it as a tool failure.",
        "runtime_context": "LangGraph 1.2.6, Python 3.12, a ToolNode configured with both awrap_tool_call middleware and handle_tool_errors, executing a tool that calls interrupt() to pause for human approval.",
        "evidence": "Removing awrap_tool_call restores correct suspension behavior, and the printed output literally shows the Interrupt payload stringified inside a 'Tool call failed (GraphInterrupt)' error message rather than the graph halting."
      },
      "evidence_gaps": [],
      "artifact": {
        "path": "docs/failure-cases/graphinterrupt-not-re-raised-in-awrap-tool-call-wrap-292e4a05d8.html",
        "url": "https://impartshadow.github.io/echo-site/failure-cases/graphinterrupt-not-re-raised-in-awrap-tool-call-wrap-292e4a05d8.html"
      },
      "commercial_action": {
        "label": "Request a private agent failure audit",
        "audit_request_url": "mailto:impartshadow@gmail.com?subject=Private%20agent%20failure%20audit%3A%20graphinterrupt-not-re-raised-in-awrap-tool-call-wrap-292e4a05d8&body=Artifact%3A%20https%3A//impartshadow.github.io/echo-site/failure-cases/graphinterrupt-not-re-raised-in-awrap-tool-call-wrap-292e4a05d8.html%0A%0AFailure%20summary%3A%0AProduction%20context%3A%0AObserved%20agent%20action%3A%0AExpected%20boundary%3A%0AAvailable%20evidence%3A%0AWhat%20outcome%20would%20make%20this%20useful%20within%207%20days%3A",
        "subscription_url": "https://echofromshadow.substack.com/subscribe?utm_source=failure_case&utm_medium=public_artifact&utm_campaign=agent_failure_audit",
        "routing_rule": "Use the public artifact for broadly useful redacted failures; use the private audit path when the evidence is sensitive or the operator needs a concrete fix, guardrail, or receipt proof."
      },
      "next_proof_step": "Convert the submitted failure into the smallest reusable control: a deterministic guardrail, receipt proof, public autopsy entry, or private diagnosis if evidence is sensitive.",
      "public_safety": {
        "redaction": "emails, obvious API tokens, and long opaque secrets are redacted before publish",
        "status": "public-safe stub; deeper diagnosis still requires evidence review"
      }
    }
  ],
  "urls": {
    "html": "https://impartshadow.github.io/echo-site/failure-cases/index.html",
    "json": "https://impartshadow.github.io/echo-site/failure-cases/index.json",
    "bounty": "https://impartshadow.github.io/echo-site/agent-failure-bounty.html",
    "intake": "https://impartshadow.github.io/echo-site/failure-intake.html"
  },
  "rule": "Every qualifying external production-agent failure should become a redacted case artifact or a private-audit handoff."
}
