안티 패턴: 일반적인 오류 메시지
'Operation failed'가 복구 결정을 방해하는 이유를 알아봅니다
안티 패턴: 일반적인 오류 메시지은(는) CoddyKit의 무료 Claude Architect 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Claude Architect 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
The Failure That Tells You Nothing
A tool in your agentic loop returns:
{ "status": "error", "message": "Operation failed" }
The model now has to decide: retry, try a different query, escalate to a human, or stop. But "Operation failed" carries zero signal. It cannot tell whether the database was briefly down, the input was malformed, or a business rule blocked the action.
This is the generic error anti-pattern — and it quietly breaks agent reliability.
Why the Model Can't Recover
In the agentic loop, the model inspects each tool result and chooses the next action. A recovery decision depends on what kind of failure occurred:
- Transient outage → retry locally
- Bad input → fix the query and re-call
- Business rule violation → do NOT retry; escalate or stop
- Permission denied → escalate
A generic message collapses all four into one indistinguishable blob. The model is left to guess — often retrying something that will never succeed, or aborting the whole workflow on a fault it could have recovered from.
The Anatomy of a Structured Error
The fix is a structured error contract. For MCP tool results, an error should carry:
isError: true— an explicit error flagerrorCategory— one oftransient,validation,business,permissionisRetryable— whether retrying could plausibly helpmessage— human-readable detailattempted_query— what the tool actually triedpartial_results— anything usable already gathered
Each field maps directly to a recovery decision the model can now make deterministically.
error_result = {
"isError": True,
"errorCategory": "transient", # transient | validation | business | permission
"isRetryable": True,
"message": "Inventory DB connection timed out after 5s",
"attempted_query": "SELECT stock FROM inventory WHERE sku='A-117'",
"partial_results": []
}Category Drives the Recovery Route
The single most important field is errorCategory. It tells the model which branch of the recovery tree to take:
transient→ retry locally in the subagent (network blip, timeout)validation→ the input was wrong; correct the arguments and re-callbusiness→ a rule blocked it (e.g. refund over limit); retrying is pointless — escalatepermission→ caller lacks access; escalate, never loop
"Operation failed" forces the model to infer the category from prose, if it can at all. A typed category removes the guessing.
isRetryable: Stop the Pointless Loop
A generic error invites a dangerous behavior: blind retrying. The model re-calls the failing tool, gets "Operation failed" again, and may burn iterations on something that can never succeed.
An explicit isRetryable flag turns a guess into a rule. The model retries only transient faults, and immediately routes business and permission failures elsewhere.
Remember: iteration caps are a safety net, never the primary stop mechanism. Clean error signals are what actually keep the loop honest.
def handle_tool_error(err):
if err["errorCategory"] == "transient" and err["isRetryable"]:
return retry_locally(err["attempted_query"])
if err["errorCategory"] == "validation":
return fix_arguments_and_recall(err)
# business / permission: retrying never helps
return escalate_to_human(err)Access Failure vs. Empty Result
A subtle trap: a generic error blurs the line between "I could not look" and "I looked and found nothing".
- Access failure (timeout, auth error) → the data may exist; retry or escalate.
- Valid empty result (zero matching rows) → the answer is genuinely "none"; do NOT retry.
If both surface as "Operation failed," the model might retry forever on an empty set, or give up on a recoverable outage. A structured contract keeps these two outcomes distinct.
# Valid empty result is NOT an error:
{ "isError": False, "results": [], "message": "No orders found for customer C-908" }
# Access failure IS an error:
{ "isError": True, "errorCategory": "transient", "isRetryable": True,
"message": "Order service returned 503" }Carry Partial Results Forward
Failures are rarely all-or-nothing. A multi-agent research coordinator might fan out to five sources; one times out. Returning a bare "Operation failed" throws away the four that succeeded.
Include partial_results so the model can synthesize what it has and annotate the gap rather than abort. The architect's rule: recover transient faults locally, and when you must escalate, escalate with partial results attached — never silently suppress and never kill the whole workflow over one failed branch.
{
"isError": True,
"errorCategory": "transient",
"isRetryable": True,
"message": "2 of 5 sources timed out",
"partial_results": [
{"source": "arxiv", "finding": "..."},
{"source": "pubmed", "finding": "..."}
],
"alternatives": ["retry timed-out sources", "report coverage gap"]
}attempted_query Enables Self-Correction
When the failure is a validation error, the model needs to know what it sent to fix it. The attempted_query field echoes the exact input back.
This mirrors retry-with-feedback for structured output: you fix format and structural errors by sending the model the original request plus the exact error plus what was attempted. With attempted_query in hand, the model can spot a malformed SKU or a bad date filter and correct the next call — instead of repeating the same broken request.
{
"isError": True,
"errorCategory": "validation",
"isRetryable": True,
"message": "Date filter must be ISO-8601; got '06/2026'",
"attempted_query": "orders?since=06/2026"
}Structured Errors vs. Deterministic Guarantees
Structured errors make the model's recovery smarter, but they are still consumed by a probabilistic model. For failures with financial, legal, or safety consequences, pair them with deterministic enforcement.
A PostToolUse hook can inspect a tool result before the model ever sees it, and an outgoing-call hook can block a policy-violating action (e.g. a refund over $500) with 100% determinism. Prompts and structured signals guide ~90% of the time; hooks guarantee the rest. Structured errors enable intelligent routing — they do not replace hard guardrails.
Prefer Community Servers — and Their Error Shapes
You usually do not have to invent this contract from scratch. For standard integrations, prefer well-maintained community MCP servers over custom ones — they often already return categorized, retryable errors.
When you do build a custom tool, document the error contract in the tool description alongside purpose, return values, input formats, and edge cases. A good description tells the model not just how to call the tool, but how to interpret what comes back when things go wrong.
Don't Hide Errors in the Long Middle
One more reliability angle: where the error lands in context matters. Models attend most to the start and end of context and least to the middle ("lost in the middle").
So beyond making errors structured, keep them tight: trim verbose tool output to the relevant fields and surface the error signal cleanly rather than burying errorCategory inside a wall of stack trace. A concise, typed error near the point of decision beats a verbose one drowned in noise.
Quick Check: Diagnosing a Generic Error
A research coordinator delegates to a subagent whose database lookup tool returns { "status": "error", "message": "Operation failed" }. The coordinator keeps re-calling the tool and eventually aborts the entire report.
Recap: Make Failures Actionable
Generic errors like "Operation failed" block recovery because they hide the one thing the model needs — what kind of failure it was.
- Return structured errors:
isError,errorCategory(transient/validation/business/permission),isRetryable,message,attempted_query,partial_results. - Category drives routing: retry transient, fix validation, escalate business/permission.
- Distinguish access failure from a valid empty result.
- Carry partial results forward; never silently suppress or abort the whole workflow on one branch.
- For financial/legal/safety stakes, back structured errors with deterministic hooks.
Structured errors turn a dead end into an intelligent recovery decision.
자주 묻는 질문
“안티 패턴: 일반적인 오류 메시지” 강의는 무료인가요?
네 — “안티 패턴: 일반적인 오류 메시지” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Claude Architect 강의 전체를 잠금 해제할 수 있습니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.
“안티 패턴: 일반적인 오류 메시지”에서 뭘 배우나요?
'Operation failed'가 복구 결정을 방해하는 이유를 알아봅니다 브라우저에서 직접 실행하는 실습 코드로 Claude Architect을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Claude Architect을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Claude Architect은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“안티 패턴: 일반적인 오류 메시지” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Claude Architect 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Claude Architect 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- isError 플래그
- 오류 범주
- 재시도 가능 메타데이터 및 부분 결과
- 안티 패턴: 일반적인 오류 메시지