Repository navigation
Improve CI-fix Azure queue failure diagnostics - #39243
Conversation
Preserve bounded, redacted Azure response details for nontransient queue failures so provider-side HTTP 400 validation errors are actionable without changing queue retry or acceptance semantics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2c742a56-d991-47ed-801b-52bec21dc948
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 39243Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 39243" |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
🟡 Changes recommended
Valid JSON fallback responses can expose token fields written as JSON key-value pairs.
1 open finding
What changed in this PR
Improves Azure DevOps queue-failure diagnostics while preserving existing fail-closed behavior.
Changes:
- Extracts, bounds, and redacts Azure error details.
- Adds tests for structured and plain-text failures.
- One JSON credential-redaction gap remains.
| File | Description |
|---|---|
.github/scripts/Queue-CiFixAzdoValidation.ps1 |
Formats queue failure diagnostics. |
.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 |
Tests diagnostics and retry safety. |
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
This comment has been minimized.
This comment has been minimized.
|
/azp run |
|
Azure Pipelines: 3 pipeline(s) were filtered out due to trigger conditions. |
|
Note 🔍
|
Cover valid JSON fallback responses whose quoted credential fields previously bypassed assignment-style redaction, while preserving safe diagnostic fields and existing queue failure semantics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2c742a56-d991-47ed-801b-52bec21dc948
This comment has been minimized.
This comment has been minimized.
|
/azp run |
|
Azure Pipelines: 3 pipeline(s) were filtered out due to trigger conditions. |
|
Note 🔍
|
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@PureWeen — new AI review results are available based on commit
4921c90.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ⚠️ SKIPPED
No tests were detected in this PR.
Recommendation: Add tests to verify the fix using the write-tests-agent.
📋 Pre-Flight — Context & Validation
Pre-Flight Context
Reported problem
PR #39243 is a diagnostics-only change for the trusted CI-fix dispatcher. A bot-created PR canary reportedly reached the native maui-pr queue operation, but a nontransient Azure DevOps HTTP 400 surfaced only the generic 400 (Bad Request) exception; the Azure response body containing the actionable provider validation error was discarded. The provider-side cause and restoration of queue acceptance remain unknown.
The PR references #39223, an open net11.0 CI issue where Windows and macOS Debug solution builds fail because VersionLessThan receives an empty version. That issue motivated the validation attempt but is not fixed by this PR.
GitHub metadata currently records the PR as merged into main on 2026-10-09, with final submitted head 4921c90376b788d272eec02a1ea07fcbe3f69be7.
Changed files and mechanism
The immutable diff changes exactly two PowerShell files:
-
.github/scripts/Queue-CiFixAzdoValidation.ps1- Adds
Get-AzdoQueueFailureMessage. - Reads the response text from
ErrorRecord.ErrorDetails.Message. - For JSON responses, preferentially emits Azure’s top-level
messageand optionaltypeKey; otherwise it retains bounded fallback text. - Normalizes tabs/newlines and redacts Basic/****** selected JSON credential fields, assignment-style token values, and JWT-shaped strings.
- Limits diagnostic detail to 1,024 characters by default, with an accepted range of 256–4,096 and an explicit truncation suffix.
- Preserves the original exception message when no usable response detail exists.
- Changes only the nontransient queue-POST catch path from rethrowing the original error to throwing the constructed diagnostic message. Transient classification, retries, sleeps, and duplicate-build reconciliation remain outside this new branch.
- Adds
-
.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1- Includes the new helper in the test module’s selected function set.
- Adds Pester coverage for:
- Extracting Azure
message/typeKeyfrom an HTTP 400 while retaining one POST and no duplicate lookup. - Redacting and truncating a non-JSON response.
- Redacting quoted JSON credential fields while preserving a safe field.
- Extracting Azure
- A prior review thread identified that valid JSON fallback bodies could expose quoted token fields. The final commit adds the corresponding redaction and test; that thread is marked resolved.
Security and platform-sensitive paths
The affected script is trusted CI infrastructure rather than MAUI application/runtime code. Its unchanged workflow, .github/workflows/ci-fix-azdo-validation.yml, runs PowerShell on ubuntu-latest for pull_request_target and trusted fixer workflow_run events. It sparsely checks out the trusted workflow revision, does not fetch or execute the PR head, uses persist-credentials: false, and receives Azure OIDC client settings plus a read-only GitHub token.
Relevant platform boundaries are therefore:
- GitHub Actions/Linux/PowerShell: response/error-record behavior and Pester compatibility are directly relevant.
- Azure DevOps REST provider: the diagnostic depends on how failed
Invoke-RestMethodcalls populateErrorDetails.Message. - Android: Android is the requested platform for later testing, but this diff contains no Android source or device behavior. Android validation can exercise the selected review lane only indirectly.
- Windows/macOS: these are the platforms affected by linked issue #39223. Android-only testing cannot establish whether that originating build failure is fixed, and this PR does not claim to fix it.
Validation requirements and available evidence
- Gate:
⚠️ skipped because Gate detected no tests. It must not be rerun. The submitted diff nevertheless contains Pester tests; why Gate did not classify them is unknown. - The PR description reports 82 passing Pester tests under versions 5.9.0 and 6.2.0, but those claims are tied to commit
ba0fd0e822e2dce0fce48ee3eb4e9a1f74e2d5f1, not the final head that added JSON credential redaction. - At the final head, GitHub check metadata shows
Pester scriptsconcluded failure. The failure logs/reason were unavailable through the read-only context gathered here. - The three
maui-pr,maui-pr-devicetests, andmaui-pr-uitestschecks were neutral after being filtered by trigger conditions; they do not establish functional validation. - Validation should specifically establish final-head behavior for:
- JSON and non-JSON Azure error shapes.
- Missing or whitespace-only response details.
- Malformed JSON and PowerShell-version-specific conversion exceptions.
- All intended credential forms, line normalization, and exact truncation bounds.
- One POST with no retry or duplicate reconciliation for nontransient failures.
- Unchanged transient retry/reconciliation behavior.
- Acceptability of replacing the original thrown error record with a newly constructed diagnostic exception/message.
- End-to-end emission of a useful redacted Azure response during the next legitimate automatic dispatcher execution, without manually replaying the dispatcher or issuing a direct Azure queue POST.
Remaining uncertainties
- The actual Azure response that caused the original HTTP 400 is unavailable, so its shape and provider validation message are unknown.
- The Azure queue-rejection root cause remains unproven, and successful queue acceptance has not been demonstrated.
- The reason the final-head
Pester scriptscheck failed is unknown. - The PR description’s successful test counts were not independently verified and do not cover the recorded final head.
- Whether every
Invoke-RestMethodfailure shape exposes the body throughErrorDetails.Messageis unconfirmed. - Only the response-detail portion is explicitly sanitized by the new helper; whether an exception message itself could contain sensitive material is unestablished.
- No post-merge automatic dispatcher diagnostic was available.
- This is context gathering, not an exhaustive code or security review, and it does not replace the dedicated expert review.
🔬 Code Review — Deep Analysis
Code Review — PR #39243
Independent Assessment
What this changes:
The PR adds Get-AzdoQueueFailureMessage and wires it into the nontransient queue-POST failure path in .github/scripts/Queue-CiFixAzdoValidation.ps1 so thrown errors include bounded/redacted Azure response detail (preferring JSON message + optional typeKey, else fallback body text). It also adds Pester tests for nontransient behavior preservation, truncation/redaction, and JSON credential-field redaction in fallback responses.
Inferred motivation:
Make Azure DevOps queue failures diagnosable (especially HTTP 400 validation responses) without changing the one-POST/no-retry semantics for nontransient failures.
Reconciliation with PR Narrative
Author claims: diagnostics-only change; preserve fail-closed queue semantics; redact sensitive response details; prior successful local Pester runs.
Agreement/disagreement:
- Agree on mechanism and semantics preservation in source/tests at final head.
- Final-head check evidence does not validate the narrative’s earlier passing-test claim for commit
ba0fd0e...; current head is4921c903...and has a failedPester scriptscheck.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
JSON fallback could leak {"access_token":"..."} style secrets |
Copilot inline comment on .github/scripts/Queue-CiFixAzdoValidation.ps1 |
✅ Fixed | Final head adds JSON key/value redaction regex at .github/scripts/Queue-CiFixAzdoValidation.ps1:127 and regression test at .github/scripts/Queue-CiFixAzdoValidation.Tests.ps1:1185-1204. |
No unresolved prior ❌ Error findings were found in current code.
Blast Radius Assessment
- Runs for all instances: No (only error path of CI-fix Azure queue POST failures, not app/runtime flow).
- Startup impact: No MAUI app startup impact (GitHub Actions/PowerShell infra script only).
- Static/shared state: No new mutable shared state introduced.
- Platform relevance: Requested platform was Android, but this diff is CI PowerShell/GitHub Actions + Azure DevOps boundary logic; no Android runtime/control/handler code is touched.
CI Status
gh pr checks --requiredcould not be executed due unauthenticatedghin this environment (tool-unavailable fallback applies).- Independent head check-run evidence (
4921c903...) shows:Pester scripts: completed / failureBuild Insights: in_progressmaui-pr,maui-pr-uitests,maui-pr-devicetests: neutral
- Classification: undetermined for merge-readiness (required-check view unavailable here; at least one relevant check is failed and another pending).
- Action taken: confidence capped low; no LGTM.
Findings
No new concrete actionable code defect was validated from current diff/source/tests.
Failure-Mode Probing
- Nontransient POST path: still one POST, no reconciliation/retry (guarded by
if (-not Test-IsTransientHttpException) { throw (Get-AzdoQueueFailureMessage ...) }at.github/scripts/Queue-CiFixAzdoValidation.ps1:827-829), with explicit test coverage at.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1:1131-1162. - Fallback secret redaction when JSON has no top-level
message: now includes JSON key/value credential redaction plus assignment and bearer/basic forms (.ps1:126-129), covered by test.Tests.ps1:1185-1204. - Truncation/normalization bounds: newline/tab normalization and bounded truncation with suffix remain deterministic (
.ps1:125,131-134) with explicit test (.Tests.ps1:1164-1183). - Transient reconciliation semantics: unchanged branch preserves ambiguous-POST reconciliation behavior; existing tests around “may have been accepted” flows remain present in same file.
External Output Contract
| Consumer token/pattern | Producer location | Producer emission condition | Consumer assumption | Ordinary negative case | Downstream effect |
|---|---|---|---|---|---|
ErrorRecord.ErrorDetails.Message body; JSON keys message/typeKey |
PowerShell Invoke-RestMethod failure record content from Azure DevOps queue API response body |
Present when provider returns body text in failure record details | Body may contain actionable provider diagnostics and may contain sensitive tokens needing redaction/bounds | Nontransient queue failure with non-JSON or JSON lacking top-level message |
Consumer keeps fallback text, applies normalization/redaction/truncation, throws composed diagnostic; no retry/reconciliation for nontransient failures |
| Redaction patterns for bearer/basic, assignment tokens, JSON credential fields, JWT-like strings | Same response-body source as above | Tokens appear in provider/body text | Redaction should sanitize before throw | Valid JSON fallback with credential fields but no top-level message |
Final head now redacts these values before composing thrown message |
Applicable Security / External-Output Contract Analysis
- This change is in trusted infra scripts and does not execute untrusted PR code paths directly.
- It handles external provider output (Azure response body) and now applies bounded sanitization before surfacing diagnostics.
- The previously identified JSON fallback credential leak mechanism is source-fixed and test-backed at final head.
Verdict: NEEDS_DISCUSSION
Confidence: low
Summary:
The submitted code change appears mechanically sound for its intended diagnostics behavior and preserves nontransient retry/reconciliation semantics, with the prior JSON-redaction defect now fixed and regression-tested. However, final-head CI evidence is not in a merge-ready state from this environment (Pester scripts failed; another check pending; required-check query unavailable via unauthenticated gh). Given undetermined/failing check state, this review cannot recommend LGTM.
Refinement-threshold decision (required)
Decision: NOT MET for additional code-refinement request.
Reason: after validating source and tests at final head, no concrete new actionable code defect or unresolved behavioral mechanism was established beyond CI/check-state uncertainty. Current blocker is evidence/verification state (failed/pending checks), not a newly proven implementation bug in changed lines.
🛠️ Try-Fix — Analysis & Comparison
Alternative generation: not requested
Evidence-first mode intentionally omits routine Try-Fix attempts. This is not a failed
attempt, a completed alternative search, or evidence that alternatives have no value.
The expert review may still produce one evidence-backed PR refinement.
📝 PR Finalize — Recommended Title & Description
Assessment: ✏️ Recommend updating — the implementation summary remains accurate, but the validation section applies to an earlier head and does not disclose the failed final-head Pester check.
Recommended title
[CI] CI-fix: Preserve redacted Azure queue failure diagnostics
Recommended description
### Description of Change
Preserve bounded, redacted Azure DevOps response details when the trusted CI-fix dispatcher receives a nontransient queue failure such as HTTP 400.
The first genuine bot PR canary proved native `maui-pr` integration, but the dispatcher lost Azure's response body for queue POST failures and surfaced only `400 (Bad Request)`. The provider-side queue-rejection cause remains unknown.
- `.github/scripts/Queue-CiFixAzdoValidation.ps1` adds `Get-AzdoQueueFailureMessage`, which reads `ErrorRecord.ErrorDetails.Message`, prefers Azure's top-level `message` and optional `typeKey` for JSON responses, and otherwise retains bounded fallback text. It normalizes tabs and newlines, redacts common Basic/Bearer, assignment-style, JSON credential-field, and JWT-shaped secrets, and limits diagnostic detail to 1,024 characters by default with an accepted 256–4,096 range and an explicit truncation suffix.
- The nontransient queue-POST catch path now throws the constructed diagnostic message. It still performs one POST with no retry or duplicate-build reconciliation; transient classification, retries, sleeps, and ambiguous-POST reconciliation remain unchanged.
- `.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1` adds Pester coverage for Azure `message`/`typeKey` extraction, non-JSON redaction and truncation, JSON credential-field redaction, and preservation of the nontransient one-POST behavior.
**Diagnostics-only scope:** this change does not claim to restore Azure queue acceptance. After human merge, the next legitimate automatic trusted dispatcher execution must supply the actual redacted Azure response before any payload, provider, or configuration change is justified. No manual dispatcher rerun or direct Azure queue POST is part of this validation.
### Validation
- At final submitted head `4921c90376b788d272eec02a1ea07fcbe3f69be7`, the trusted Gate was skipped because it detected no tests, despite the Pester test changes.
- The final-head `Pester scripts` check failed; the failure reason was unavailable to this review. `Build Insights` was still pending, while `maui-pr`, `maui-pr-devicetests`, and `maui-pr-uitests` were neutral after trigger filtering.
- At earlier head `ba0fd0e822e2dce0fce48ee3eb4e9a1f74e2d5f1`, the author reported 82 passing tests with Pester 5.9.0 and an independent run reported 82 passing tests with Pester 6.2.0. Those runs predate the final JSON credential-field redaction change and do not validate the final submitted head.
- The earlier-head focused review verified the two-file scope and reported `git diff --check` passing.
### Platforms
This change affects trusted GitHub Actions/Linux/PowerShell CI infrastructure and the Azure DevOps REST boundary. It does not change Android or other MAUI runtime behavior.
### Issues Fixed
Refs #39223
🏁 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Trusted evidence-first context consumed |
| Code Review | NEEDS_DISCUSSION (low) | 0 errors, 0 warnings; final-head validation remains unresolved |
| Gate | No tests detected; Gate did not establish that the submitted fix passes | |
| Try-Fix | not requested | Intentionally omitted by evidence-first mode |
| Refinement | NOT GENERATED | Expert review found no source-backed actionable defect meeting the refinement threshold |
| Report | ✅ COMPLETE | Only real candidates were compared |
Code Review and Candidate Comparison
| Candidate | Implementation evidence | Validation evidence | Assessment |
|---|---|---|---|
pr |
The submitted helper preserves nontransient one-POST/no-reconciliation behavior, bounds and normalizes provider detail, and redacts the previously identified JSON credential-field form. The expert review found no concrete implementation defect at final head. | Gate was skipped. The relevant final-head Pester scripts check failed, Build Insights was still pending, and the required-check query was unavailable; the 82-test passing claims in the PR description apply to an earlier head. |
Sole real candidate and therefore selected, but not demonstrated merge-ready. |
Try-Fix: not requested. No try-fix-N candidates were generated, and no pr-plus-reviewer refinement was implemented because the expert evidence threshold was not met.
Selected Fix: PR — it is the only implemented candidate and the expert review found no source-backed reason to alter it. This selection does not override the skipped Gate or unresolved final-head check evidence.
Summary
Request changes because the submitted PR lacks successful final-head validation: Gate was skipped, the relevant Pester check failed, and another check remained pending. The expert review classified the code as NEEDS_DISCUSSION with low confidence rather than identifying a defect suitable for a reviewer patch.
Root Cause
The trusted CI-fix dispatcher discarded the actionable Azure DevOps response body when a nontransient queue POST failed, leaving only the generic HTTP exception text. The provider-side reason for queue rejection remains unknown and is outside this diagnostics-only change.
Fix Quality
The submitted fix is focused and preserves the existing fail-closed queue semantics while adding bounded, normalized, redacted provider diagnostics and targeted Pester coverage. Its implementation appears sound from source inspection, including the final JSON credential-field redaction, but failed or missing final-head validation prevents an approval recommendation.
🧭 Next Steps — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.

Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
Preserve bounded, redacted Azure DevOps response details when the trusted CI-fix dispatcher receives a nontransient queue failure such as HTTP 400.
The first genuine bot PR canary proved native
maui-printegration but the dispatcher lost Azure's response body for all queue POST failures, leaving only400 (Bad Request). This change extracts Azure'smessageandtypeKeywhen available, bounds fallback response text, redacts common credential forms, and keeps the existing fail-closed behavior: one POST, no retry or reconciliation for nontransient failures.Diagnostics-only readiness: this change is ready for human deployment, but it does not claim to restore Azure queue acceptance. The provider-side HTTP 400 root cause remains unproven and queueing is not restored. After human merge, the next legitimate automatic trusted dispatcher execution must supply the actual redacted Azure response before any payload, provider, or configuration change is justified. No manual dispatcher rerun or direct Azure queue POST is part of this validation.
Validation at exact head
ba0fd0e822e2dce0fce48ee3eb4e9a1f74e2d5f1:mainverified before and after the run.git diff --check: passed.Issues Fixed
Refs #39223