At 2:12 PM on Friday, /api/orders starts throwing 500 errors. Dashboards turn red, and a message pops up in Slack: “Should we rollback the deployment?” These days, another line gets added: “I asked the AI agent first, and it says it’s a DB issue.”
That answer was only half right. This article takes empirical data from three 2026 cloud Root Cause Analysis (RCA) papers quantifying where and how AI fails, applying those findings directly to the Azure outage scenario above to build a step-by-step sequence down to the true root cause. If Four Ways Troubleshooting Changed in 2026 was about the tools, this article is about the process required after picking up those tools.
1. Problem Statement: 500 Is a Symptom, Not a Cause
The stack is a common architecture. An App Service (Orders API) sits behind Azure Front Door, connected to a single Azure SQL Database behind it, authenticating via Managed Identity by acquiring Microsoft Entra ID tokens to connect to the DB. Observability data is collected in Application Insights and Log Analytics.
There are only three observed facts:
- From 14:12, the 5xx error rate for
/api/ordersjumped from 2% to 60%. - At the exact same time, Azure SQL DTU utilization hit 98%.
- The last deployment took place 3 days ago.
If you feed this to an AI agent, it will usually reply: “Root cause is DB overload; add missing indexes.” In fact, the example given in Microsoft’s Azure SRE Agent documentation follows this exact pattern: Hypothesis 1 (deployment) rejected, Hypothesis 2 (DB overload) verified, root cause is a missing index. Neat and clean. The problem is that this outage is not that simple. In this case, 98% DTU is not the root cause, but a secondary symptom.
2. Failures Quantified by Research: Swapping Models Doesn’t Fix It
A study published earlier this year by researchers from Hanyang University and OKESTRO, titled Why Do AI Agents Systematically Fail at Cloud Root Cause Analysis?, ran 335 incidents from the OpenRCA benchmark across five models (Gemini 2.5 Pro, GPT-5 mini, GPT-OSS 120B, Solar Pro 2, and Claude Sonnet 4) for a total of 1,675 runs. Humans manually classified the reasoning logs of failed executions. The top failure modes were as follows:
| Failure Pattern | Rate | Manifestation in Outages |
|---|---|---|
| Fabricating interpretation | 71.2% | Filling missing data with narrative causalities |
| Skipping exploration | 63.9% | Completely bypassing essential metrics or components |
| Declaring symptoms as root cause | 39.9% | Stopping investigation at surface-level anomalies |
| Query code error | 27.2% | Executed code is wrong, but output looks plausible |
| Telemetry cherry-picking | 26.9% | Drawing conclusions from a single data type |
| Temporal misalignment | 23.3% | Overlapping different time windows and calling it correlation |
| Lack of cross-validation | 18.6% | Accepting a single output without verification |
The conclusion of this paper is crucial: this distribution repeated almost identically across all five models. This means upgrading to a more expensive model will not solve the issue. The same study notes that instruction-code mismatches (20–26%) occurred when the controller passed only natural language summaries to the executor. Passing the generated code along with the full execution results reduced related failures by up to 15 percentage points and shortened execution time by 22.3%. In short, it is the architecture, not the prompt, that needs fixing.
Another paper points in the same direction. Published in January, Stalled, Biased, and Confused evaluated six models across ReAct and Plan-and-Execute workflows across 48,000 incident scenarios spanning 228 days of execution, categorizing 16 types of RCA reasoning failures. The title sums it up: agents get stalled, biased toward initial hypotheses, and confused by signals.
Conversely, the papers also show what actually worked. In December 2025, PRAXIS treated the LLM not as a free-roaming investigator, but as a policy navigating a graph. By constraining the agent to traverse defined service-dependency and code-dependency graphs, accuracy rose up to 6.3x compared to the ReAct baseline, while token usage was cut to one-fifth (5.3x reduction). Constraining the search space won.
3. Translating Research Recommendations into an Investigation Workflow
Four common rules emerge across all three papers. They apply equally whether a human or an AI agent conducts the investigation.
- Anchor the start time first. Before analyzing correlations, establish the exact minute each signal began degrading. This eliminates temporal misalignment (23.3%).
- List hypotheses with explicit rejection criteria upfront. Note “what result causes us to discard this hypothesis” right next to the query. This is the cheapest defense against bias and confusing symptoms with root causes.
- Never conclude based on a single signal type. Only proceed when at least two telemetry types—among metrics, logs, traces, and change history—point in the same direction.
- When a candidate cause appears, go one hop deeper. If you cannot answer “Why did that happen?”, you are still looking at a symptom.
4. Practical Application: Four Hypotheses and Rejection Criteria
Applying this directly to the incident scenario above, each hypothesis includes a query and rejection criterion.
Hypothesis 1 — A recent deployment or configuration change broke it. Rejection criterion: No write operations occurred within 6 hours prior to the error start time.
AzureActivity
| where TimeGenerated > ago(24h)
| where OperationNameValue has_any (
"MICROSOFT.WEB/SITES/WRITE",
"MICROSOFT.WEB/SITES/CONFIG/WRITE",
"MICROSOFT.SQL/SERVERS/DATABASES/WRITE")
| project TimeGenerated, Caller, OperationNameValue, ActivityStatusValue, _ResourceId
| order by TimeGenerated desc
Results showed the last change was 3 days ago. Rejected. Rolling back deployments is taken off the table at this point.
Hypothesis 2 — Upstream platform outage. Rejection criterion: No Resource Health or Service Health events during the timeframe.
az monitor activity-log list
--resource-group rg-orders
--start-time 2026-09-04T04:30Z
--query "[?category.value=='ResourceHealth' || category.value=='ServiceHealth'].{t:eventTimestamp, cat:category.value, status:properties.currentHealthStatus}"
-o table
Output was empty. Rejected. However, platform advisories are frequently delayed, so check back once more before wrapping up the investigation.
Hypothesis 3 — Database saturation. Rejection criterion: DTU and session metrics remain within normal baseline levels at the error start time.
AzureMetrics
| where TimeGenerated between (ago(6h) .. now())
| where ResourceProvider == "MICROSOFT.SQL"
| where MetricName in ("dtu_consumption_percent", "sessions_percent", "connection_failed")
| summarize avg_v = avg(Average), max_v = max(Maximum)
by MetricName, bin(TimeGenerated, 5m)
| order by TimeGenerated asc
DTU hit 98%, session usage reached 95%, connection failures spiked. Verified. Stopping here, however, commits the 39.9% failure pattern identified in the papers. Apply Rule 4: Why did DTU spike?
Hypothesis 4 — The application is hitting the DB at a much higher frequency than usual. Rejection criterion: The ratio of DB calls to incoming requests remains unchanged from normal baseline levels.
// App Insights: 요청 1건당 DB 호출이 몇 번인가
let win = 6h;
let reqs = requests
| where timestamp > ago(win)
| summarize req = count() by bin(timestamp, 5m);
dependencies
| where timestamp > ago(win)
| where type has "SQL"
| summarize dep = count(), fails = countif(success == false),
p95 = percentile(duration, 95) by bin(timestamp, 5m)
| join kind=inner reqs on timestamp
| extend calls_per_request = round(todouble(dep) / req, 2)
| project timestamp, req, dep, calls_per_request, fails, p95
| order by timestamp asc
Request volume stayed flat, but DB calls per request jumped from 3.1 to 12.4. This indicates active retry loops. What are they retrying? Connection acquisition failures. Since the application uses Managed Identity to obtain tokens for SQL authentication, go one hop deeper.
// 관리 ID 로그인은 SigninLogs 가 아니라 이 테이블이다
// (Entra ID 진단 설정에서 ManagedIdentitySignInLogs 를 보내야 쌓인다)
AADManagedIdentitySignInLogs
| where TimeGenerated between (ago(6h) .. now())
| where ServicePrincipalName has "orders-api"
| summarize total = count(), failed = countif(ResultType != 0)
by bin(TimeGenerated, 5m)
| order by TimeGenerated asc
Starting at 14:10, service principal token requests spiked from 40 to 900 per minute, with a large portion failing due to throttling.
The reconstructed causal chain is as follows: The moment token cache expires, a code path that instantiates a new credential object per request is triggered → Token issuance gets throttled → Connection acquisition fails, triggering a surge of retries → Connection pools and sessions exhaust, driving DTU usage up to 98% → Front-end starts throwing 500 errors. In short, the root cause was two hops away from the initial symptom. Adding an index would have done nothing, and scaling up the database SKU would have only prolonged the outage while inflating costs.
Remediation happens in two steps. Mitigation involves applying exponential backoff with jitter and max retries, alongside lowering connection pool limits. The permanent root-cause fix is reusing credential objects (such as DefaultAzureCredential) across requests instead of instantiating them per request, preserving token caching.
5. What to Delegate to AI Agents
The research consensus is not “don’t use AI agents,” but rather “have humans define the exploration boundary.” What PRAXIS accomplished using graphs, we enforce through process.
- Delegate: Timeline alignment, change log aggregation, drafting cross-table queries, and generating incident timeline/report drafts.
- Withhold: Final causal determination. Humans declare when an investigation is officially closed.
- Require: For every conclusion, mandate the inclusion of supporting queries and their raw execution results. Any statement lacking attached evidence is inherently a candidate for the 71.2% hallucination failure mode.
- Prohibit: Production write permissions. Attach agents with read-only access, and keep remediation execution in human hands.
If you use Azure SRE Agent, connect your source control repository and upload knowledge documents first. If the agent cannot access code and past incident history, it defaults to reasoning over surface-level metrics just like the failure patterns described above.
6. Actionable Steps for This Week
- Pin a hypothesis-rejection template in your incident management channel: One line for the hypothesis, one query, one line for the rejection criterion.
- Save four core KQL queries (Activity Logs, Health Events, Resource Metrics, Dependency Calls per Request) as saved functions/queries.
- Add “DB Calls per Request” to your main dashboard—it is the fastest indicator of retry storms.
- Audit codebases for code paths that instantiate token/credential objects on every request.
- Add “How many hops deep did we stop?” as a standard item in incident post-mortems.
⚠️ The outage in this article is a reconstructed scenario combining public Azure documentation and failure patterns from the referenced research papers, not an actual customer incident. Table and metric names in KQL and CLI examples may vary depending on diagnostic settings and SDK versions, so verify your workspace schema beforehand. Figures cited from research reflect results within specific benchmarks (e.g., OpenRCA) and do not guarantee identical performance in production environments.
Frequently Asked Questions
How much should I trust an AI agent’s root cause analysis?
It is safest to trust only the conclusions that are accompanied by supporting queries and their raw execution results. The most frequent failure mode observed in 2026 research was fabricating narrative causality for missing data (71.2%), a failure rate that hardly decreased even with higher-tier models. Focus your review on the evidence chain rather than the conclusion statement.
If DTU hits 98%, shouldn’t we just scale up the database?
That action is only appropriate when resource exhaustion is the root cause. In cases like this scenario where a retry storm caused the exhaustion, scaling up merely prolongs the outage duration while inflating costs. When a candidate root cause emerges, ask “Why did that happen?” once more. If you cannot answer it, you are still looking at a symptom.
Does this process still make sense if our telemetry data is sparse?
Yes, though execution order matters. Activity logs and resource metrics begin collecting as soon as diagnostic settings are enabled, allowing Hypotheses 1–3 to be validated right away. Seeing dependency calls per request (as in Hypothesis 4) requires Application Insights instrumentation, so you can prioritize that telemetry gap accordingly.

Leave a Reply