Repository navigation
fix(managed-agent): name a message-less row failure in the scan summary (#15700 item 4) - #15725
Conversation
The bounded summary recorded only getMessage(), so an additional row failure whose exception carried no message rendered as failure=null with no class â and since the summary is the only record of a non-first failure, that row's failure had no identity anywhere (#15700 item 4). Name the class in that case. Pinned by a test that fails on the previous wording (the tick logged failure=null) and passes now.
|
|
|
Thanks for the PR! Template looks good â â all required sections present, Risk & Scope filled in, and the äļæčŊīæ is a complete parallel translation rather than a summary. Problem: real, and I verified it in the code rather than taking the description's word for it. In Direction: aligned. This is diagnostics quality on a failure summary that exists precisely to illuminate a sustained outage â the case where a Size: not applicable. Neither changed path matches the core-module patterns ( Approach: this is what I would have written. Read from the problem statement alone, my proposal was to fall back to the exception's class name at the sample-construction site and nowhere else â which is exactly the shape here. Scope is minimal: one expression, one behaviour, one pinning test, no drive-by edits or formatting churn, and no attempt to fold in the neighbouring deferred items (R2-3's sample/count population mismatch sits three lines away and is correctly left alone). Two small non-blocking thoughts go in the code review below rather than here. Risk: no elevated risk signals. Neither file matches the revert-correlated high-risk paths, so no extra review depth is warranted beyond the normal pass. Moving on to code review. ð äļæčŊīææč°ĒčīĄįŪïž æĻĄæŋåŪæī â ââ åŋ éįŦ čé―å ĻïžRisk & Scope å·ēåĄŦåïžäļæčŊīææŊéæŪĩåŊđåšįåŪæīįŋŧčŊčéæčĶã éŪéĒïžįåŪååĻïžåđķäļææŊčŊŧäŧĢį įĄŪčŪĪįïžäļæŊį
§æ PR æčŋ°ã æđåïžåŊđé―ã čŋæŊéåŊđåĪąčīĨæąæŧįčŊæčīĻéæđčŋïžččŊĨæąæŧååĻįæäđæĢæŊį
§äšŪæįŧæ§æ
é ââ æ°æ°æŊ č§æĻĄïžäļéįĻã äļĪäļŠæđåĻč·Ŋåūé―äļåđé
æ ļåŋæĻĄåč§åïž æđæĄïžčŋå°ąæŊæäžåįæ ·åã åŠįéŪéĒæčŋ°æķïžæįæđæĄå°ąæŊãåĻæ ·æŽæé åĪåéå°åžåļļįąŧåïžäļäŧ æĪäļåĪãââ äļæŽ PR åŪå Ļäļčīãčåīæå°åïžäļäļŠčĄĻčūūåžãäļäļŠčĄäļšãäļäļŠéä―æĩčŊïžæēĄæéĄšææđåĻææ žåžååŠå̰ïžäđæēĄæčŊåūæįļéŧįåŧķåéĄđäļčĩ·åĄčŋæĨïžR2-3 įæ ·æŽ/čŪĄæ°åĢåūäļäļčīå°ąåĻäļčĄäđåĪïžčĒŦæĢįĄŪå°įįæēĄåĻïžãäļĪæĄééŧåĄįå°æģæģæūåĻäļéĒįäŧĢį åŪĄæĨéã éĢéĐïžæ åįš§éĢéĐäŋĄå·ã äļĪäļŠæäŧķé―äļåđé äļ revert įļå ģįéŦéĢéĐč·Ŋåūïžå æĪéĪåļļč§åŪĄæĨåĪäļéčĶéĒåĪįåŪĄæĨæ·ąåšĶã čŋå ĨäŧĢį åŪĄæĨ ð â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
Code reviewNo Critical findings, and no AGENTS.md violations. The change is correct and it stays inside its own scope. Details worth recording, since a green suite on a 9-line diff proves very little on its own: The fix is right, and it changes nothing else. The ternary's true branch is byte-for-byte the old expression, so a failure that does carry a message renders exactly as before â the counting ( No new null surface. The new test is deterministic â I checked this specifically, because it looks racy and it isn't. The production scanner pool is The test is load-bearing, not decorative. Against the unpatched construction the sample holds Three non-blocking notes â none of them a reason to hold the PR:
The three-line comment in Test evidenceThis is an unattended CI run, so per the review rules I did not build, run, or execute anything from this PR â no Snapshot at review time â 38 check-runs on Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / æŊäļŠæĢæĨåäļčĄïžåææ°äļæŽĄčŋčĄïžïžįįĨ skippedïžåĪąčīĨéĄđæåĻæåã The one red check is runner infrastructure, not this PR. The step immediately before it succeeded ( Not verified, and explicitly so: the Java leg that runs this module's unit/H2 suite, Checkstyle and SpotBugs ( Sandboxed verification would settle one specific thing: äļæčŊīæäŧĢį åŪĄæĨæ Critical éŪéĒïžäđæēĄæčŋå AGENTS.mdãæđåĻæŊæĢįĄŪįïžåđķäļäļĨæ žåŪä―čŠå·ąįčåīãäŧĨäļå įđåžåūčŪ°å―ïžå äļš 9 čĄ diff äļįįŧŋčēæĩčŊæŽčšŦčŊīæäļäšåĪŠåĪïž äŋŪåĪæŽčšŦæĢįĄŪïžäļæēĄæéĄšåļĶæđååŦįčĄäļšã äļå
čĄĻčūūåžįįåæŊäļæ§čĄĻčūūåžéåäļčīïžæäŧĨåļĶ message įåĪąčīĨæļēæåŪå
Ļäļå ââ čŪĄæ°ïž æēĄæåžå
Ĩæ°įįĐšæééĒã æ°æĩčŊæŊįĄŪåŪæ§į ââ æįđææĨäščŋįđïžå äļšåŪįčĩ·æĨåįŦæïžåŪé
äļæŊã įäš§æŦæåĻįšŋįĻæą æŊ čŋäļŠæĩčŊæŊææįïžäļæŊæčŪūã åĻæŠäŋŪåĪįæé äļæ ·æŽæŊ äļæĄééŧåĄæč§ ââ é―äļæŊåä―čŋäļŠ PR įįįąïž
æĩčŊčŊæŪčŋæŊæ äššåžåŪį CI čŋčĄïžå æĪæåŪĄæĨč§åææēĄææåŧšãčŋčĄææ§čĄæŽ PR įäŧŧä―å
åŪđ ââ æēĄæ åŪĄæĨæķįđįåŋŦį
§ ââ åŊäļįįšĒ check æŊ runner åšįĄčŪūæ―éŪéĒïžäļæŽ PR æ å
ģã æŠéŠčŊïžåđķæįĄŪčŊīæïžčŋčĄæŽæĻĄå unit/H2 åĨäŧķãCheckstyle äļ SpotBugs į Java legïž æēįŪąéŠčŊåŊäŧĨįĄŪåŪäļäŧķå
·ä―įäšïž â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Confidence: 4/5 â clean across every stage; the three notes above are polish, not doubt about the change. Stepping back: this is what a well-behaved follow-up looks like. A nine-item deferred list was recorded in an open issue instead of being folded into #15632 while that PR was still under review; one item â the only one that lost information rather than reformatting it â got picked up on its own, with the pinning test the issue asked for, and the other eight were explicitly left alone. That is the "don't let review rounds balloon the PR" rule being followed rather than quoted at. My independent proposal, written from the problem statement before I read the diff, was to fall back to the exception class name at the sample-construction site and nowhere else. The PR does exactly that. I did not find a simpler path it missed, and I looked for one â the ternary is the whole change, and the alternatives I can name ( The part I would normally worry about on a diff this small is a test that passes for the wrong reason, so I went after it directly. Two things I expected to be problems turned out not to be: the What I am not confident about is the one thing I could not check: the Java leg that runs this module's unit/H2 suite, Checkstyle and SpotBugs was still in flight at review time, so the new test's real result is unconfirmed. I am not going to approve against a result that does not exist yet, and I am not going to treat the author's local macOS run as a substitute for it â hence the deferral below rather than an approval now. The red So: approve on the merits, deferred until CI lands green on the reviewed commit. The three notes â the empty-message edge, the one missing assertion for the unchanged rendering, and the absent Validation-register entry that duplicates #15700 item 8's gap â are all fine to handle here or in the item 8 follow-up. None of them should hold this up. äļæčŊīæäŋĄåŋåšĶïž4/5 ââ åéķæŪĩé―åđēåïžäļéĒäļæĄæč§åąäšæįĢĻïžčäļæŊåŊđæđåĻæŽčšŦįæįã éäļæĨįæīä―ïžčŋæŊäļäļŠč§čįåįŧ PR čŊĨæįæ ·åãäđéĄđåŧķåæļ åčĒŦčŪ°å―åĻäļäļŠ open issue éïžčäļæŊåĻ #15632 čŋåĻčŊåŪĄæķåĄčŋéĢäļŠ PRïžéååįŽååšäļéĄđ ââ å ķäļåŊäļãäļĒäŋĄæŊãčéãæĒäļŠæ žåžãįäļéĄđ ââ åđķæčŊĨ issue įčĶæąé äļéä―æĩčŊïžå ķä―å ŦéĄđæįĄŪäŋæäļåĻãčŋæŊåĻįæĢæ§čĄãäļčĶčŪĐčŊåŪĄč―ŪæŽĄæ PR æåΧãčŋæĄč§åïžčäļåŠæŊåžįĻåŪã åĻčŊŧ diff äđåïžææ đæŪéŪéĒæčŋ°åäļįįŽįŦæđæĄå°ąæŊïžåĻæ ·æŽæé åĪåéå°åžåļļįąŧåïžäļäŧ
æĪäļåĪãæŽ PR æĢæŊåĶæĪãææēĄææūå°åŪæžæįæīįŪč·ŊåūïžčäļæŊčŪĪįæūčŋį ââ äļå
čĄĻčūūåžå°ąæŊå
ĻéĻæđåĻïžæč―æģå°įæŋäŧĢåæģïž åŊđčŋäđå°į diffïžæéåļļäžæ
åŋįéŪéĒæŊãæĩčŊå éčŊŊįåå éčŋãïžæäŧĨæįīæĨåŧæĨäščŋäļįđãäļĪäļŠæéĒæäžæäļšéŪéĒįå°æđæįŧé―äļæŊïž æäļįĄŪåŪįæŊåŊäļäļäŧķæĨäļäšįäšïžčŋčĄæŽæĻĄå unit/H2 åĨäŧķãCheckstyle äļ SpotBugs į Java leg åĻåŪĄæĨæķäŧåĻčŋčĄïžæäŧĨæ°æĩčŊįįåŪįŧæå°æŠįĄŪčŪĪãæäļäžåŊđįäļäļŠčŋäļååĻįįŧæåŧ approveïžäđäļäžæŋä―č
æŽå°į macOS čŋčĄæĨæŋäŧĢåŪ ââ å æĪäļéĒæŊåŧķåčéįŦåģæđåãįšĒčēį įŧčŪšïžå°ąæđåĻæŽčšŦččĻæđåïžä―åŧķåå° CI åĻčĒŦåŪĄ commit äļč·įŧŋãäļæĄæč§ ââ įĐš message įčūđįãįžšå°įéĢäļæĄãæļēæäļåãæčĻãäŧĨåéåĪäš #15700 įŽŽ 8 éĄđįžšåĢį Validation register æŠįŧčŪ° ââ æūåĻæŽ PR åĪįæįįŧįŽŽ 8 éĄđįåįŧé―åŊäŧĨïžé―äļåšæäļšåä―åŪįįįąã â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at Approval deferred until CI lands green on |
|
Qwen Code review was cancelled before a review could be posted. Nothing failed and nothing is retried automatically: the run was cancelled â by an operator, an upstream event, or the job exceeding its execution time limit. If you still want a review of this PR, request one with |
|
The original cache-permission failure is historical: job 122259155192 on ecs-qwen-hz4-30 failed at 2026-10-27 14:46:06Z. The current cache directory was created later, at 16:28:48Z. At 17:24:04â05Z we directly tested all six hz hosts using each actual github-runner account: create a unique temporary directory under the existing npm cache, write/read back a fixed marker, then remove only those probe files. All six passed creation, write/readback and cleanup (exit 0), including hz4. No permissions or shared cache contents were changed by that validation. This PR already has attempt 2 running, so no duplicate rerun was triggered. Its current smoke job on hz1-3 has passed npm-cache configuration and font preflight and is installing dependencies: https://github.com/QwenLM/qwen-code/actions/runs/40524112310/job/122348773711 . This confirms the cache prerequisite is currently usable; full E2E result remains pending. |
|
Local verification on 2026-10-28 for head The message-less sampled failure now logs
Java commands ran in |
chiga0
left a comment
There was a problem hiding this comment.
Scope: 2 files â AutomationScanner.java (method body) and AutomationScannerTest.java (new test). Standard tier.
Findings
No blocking findings.
Approval blockers: none.
What was checked
Read
- Full context of the changed
ExecutionExceptioncatch block (lines 155â210 at head). ThefailureSamplebranch is the only site in the file callingerror.getCause().getMessage(); a grep forgetCauseandgetMessagefound no sibling site with the same pattern. - Pre-existing
commonClaimFailuresProduceOneBoundedSummary: uses only message-carrying failures throughout, so the new ternary (cause.getMessage() != null ? âĶ : cause.getClass().getName()) does not alter its path. Consistent with the PR description. - New test
aMessageLessRowFailureKeepsItsIdentityInTheSummary: correctly exercises both halves â first failure has a message and is rethrown; second is message-less and lands infailureSampleasfailure=java.lang.IllegalStateException. Assertionscontains("failure=" + IllegalStateException.class.getName())anddoesNotContain("failure=null")together pin the fix without ambiguity.
Pre-existing issue not introduced by this PR: error.getCause() itself is not null-checked before the ternary. If ExecutionException were constructed with a null cause, this would NPE as it did before. The PR does not widen this risk.
Not covered: no local working tree, so rungs 2/3 (local build / mutation probe) were not run. The change is confined to a diagnostic-string branch inside a catch block with no behavioral effect outside LOG.warn, so the coverage gap is not material.
Reviewed with AI assistance.
qqqys
left a comment
There was a problem hiding this comment.
Approving at f8d70f27e7538c0445f413cb9f6b9bd39ecf48dd.
I read the production hunk in its full context in tick() rather than from the diff alone, because the interesting questions here are whether the fallback can itself throw and whether anything beside the string moved.
No new null surface. The obvious worry with error.getCause() is a null cause turning a diagnostics path into an NPE. That dereference is not new: the pre-patch line was error.getCause().getMessage(), and failure = error.getCause() a few lines above already assigns the same value, with the rethrow chain below doing failure instanceof Error / instanceof RuntimeException. The new code hoists the identical call into a local and dereferences it exactly as before, so the assumption set is unchanged. It also holds structurally â the only source of ExecutionException here is task.getKey().get() on a FutureTask, whose run() stores a caught Throwable, so the cause is non-null on every path that reaches this catch.
Nothing but the string moved. The ternary's true branch is byte-identical to the old expression, so a failure carrying a message renders exactly as before. additionalFailures++, the failureSample.size() < 3 bound, the WARN severity and the throwable-free payload are all untouched, and the failure assignment that drives the rethrow is untouched. So the summary stays bounded in the sense #15632 established â bounded count of samples â and the only length delta is a class name replacing the literal null. getClass().getName() over getSimpleName() is right for a line whose job is identifying a cause across packages.
The pinning test is deterministic, which is not obvious from reading it. The test throws a message-carrying exception on whichever row claims first and a message-less one on the rest, then asserts the rethrown failure hasMessage("claim store down") â but the rethrown failure is the first one in join order, not execution order. Those two orders coincide only because the 5-arg test constructor (AutomationScanner.java lines 91-95) delegates with Runnable::run at line 94, so executor.execute(task) runs each task inline during the submit loop, in LinkedHashMap insertion order. Execution order therefore equals join order equals findArmed order, and row 1 gets the message-carrying throw while row 2 supplies the sampled message-less one. I confirmed this rather than assuming it, because under the 4-thread automationScannerExecutor the same assertions would be genuinely racy. logged.list having size 1 is safe for the reason the code shows: tick() is called directly, bypassing scan()'s own catch-and-warn, and additionalFailures == 1 > 0 produces exactly one WARN.
The test also fails against the unpatched construction from both directions â contains("failure=java.lang.IllegalStateException") would see null, and doesNotContain("failure=null") would trip â so it pins the change rather than merely accompanying it.
On CI: the red entries are cancellations, not failures, and the lane that validates this diff had already produced its result before being cancelled. In SDK Java (fast) / ubuntu-latest / Java 21 the log shows Running com.alibaba.qwen.code.managedagent.automation.AutomationScannerTest followed by Tests run: 71, Failures: 0, Errors: 0, Skipped: 0, and You have 0 Checkstyle violations. earlier in the same job; the job's step 11 was cancelled afterwards, with steps 15-16 skipped as a consequence. review-pr, the npm/Bun acceptance lanes and the reference-distribution lanes are likewise cancelled and have no path to a nine-line diagnostics change in a Java module. A SDK Java (full) tier run is still pending on this commit; I did not wait on it, and nothing in the completed evidence is attributable to this PR.
One adjacent item I am deliberately not raising as a blocker, since it predates this PR and is already tracked: an exception whose message is the empty string still renders failure= with nothing after it, and the sample records the first failure while the count excludes it. The first is the same rendering as before the change, so it is not a regression, and the second is #15700's item R2-3 three lines away. This PR does what it says it does â item 4 only â and leaves the rest alone.
|
Released in v0.26.3. |
|
What this PR does
One item from #15700's deferred list: the scanner's bounded failure summary now names the exception class when a non-first row failure carries no message. Previously such a row rendered as
failure=null, with no class and no other record anywhere, because the summary is the only place an additional row failure is reported.Why it's needed
#15632 replaced unbounded per-row stacks with one bounded summary. The sample it keeps is the only record of every failure except the first â so a message-less exception was recorded as the literal string
failure=null, which identifies neither the row's cause nor its type. That is the one item in the deferred list that loses information rather than merely formatting it differently.Reviewer Test Plan
How to verify
Non-UI. The change is confined to the sample string construction:
IllegalStateExceptionlogsfailure=null; with this change it logsfailure=java.lang.IllegalStateException.aMessageLessRowFailureKeepsItsIdentityInTheSummarypins both halves, and I confirmed it fails against the unpatched construction (the assertion onfailure=seesnull) and passes after;commonClaimFailuresProduceOneBoundedSummarystill passes unchanged.Evidence (Before & After)
N/A â non-UI. Before (reverted hunk, same test):
sample=[âĶ failure=claim store down, âĶ failure=null]; after:failure=java.lang.IllegalStateException. Local: both summary tests green, Checkstyle 0 violations.Tested on
Environment (optional)
OpenJDK 21, Maven 3.10.
Risk & Scope
Linked Issues
Refs #15700 â item 4 only; the other eight deferred suggestions stay deferred.
äļæčŊīæ
čŋäļŠ PR åäšäŧäđ
åĪį #15700 åŧķåæļ åäļįäļéĄđïžæŦæåĻįæįåĪąčīĨæąæŧïžåĻééĶäļŠčĄåĪąčīĨæēĄæ message æķæđäļščŪ°å―åžåļļįąŧåãæĪåčŋį§æ åĩäžæļēææ
failure=nullïžæĒæēĄæįąŧåãäđæēĄæäŧŧä―å ķåŪčŪ°å―ââå äļšæąæŧæĢæŊéĒåĪčĄåĪąčīĨåŊäļįčŪ°å―ä―į―ŪãäļšäŧäđéčĶ
#15632 įĻäļæĄæįæąæŧæŋæĒäšéčĄæ įå æ ãåŪäŋįįæ ·æŽæŊéĪįŽŽäļäļŠäđåĪæŊäļŠåĪąčīĨįåŊäļčŪ°å―ïžå æĪæ message įåžåļļčĒŦčŪ°æåéĒé
failure=nullïžæĒäļč―čŊīæåå ãäđäļč―čŊīæįąŧåãčŋæŊåŧķåæļ åäļåŊäļãäļĒäŋĄæŊãčéãæ žåžäļåãįäļéĄđãčŊåŪĄč éŠčŊčŪĄå
åĶä―éŠčŊ
é UIãæđåĻäŧ éæ ·æŽåįŽĶäļēæé ïž
IllegalStateExceptionïžæĨåŋäļšfailure=nullïžæđåĻåäļšfailure=java.lang.IllegalStateExceptionãaMessageLessRowFailureKeepsItsIdentityInTheSummaryåæķéä―čŋäļĪåïžæéŠčŊčŋåŪåĻæŠäŋŪåĪįåæģäļåĪąčīĨïžfailure=åĪäļšnullïžãäŋŪåĪåéčŋïžcommonClaimFailuresProduceOneBoundedSummaryäļåå―ąåäŧéčŋãčŊæŪïžåååŊđæŊïž
N/A ââ é UIãäŋŪåĪåïžåéčŊĨ hunkïžåäļæĩčŊïžïž
sample=[âĶ failure=claim store down, âĶ failure=null]ïžäŋŪåĪåïžfailure=java.lang.IllegalStateExceptionãæŽå°äļĪäļŠæąæŧæĩčŊéčŋïžCheckstyle 0 čŋč§ãæĩčŊåđģå°
macOS â ïžWindows / LinuxïžN/Aã
įŊåĒïžåŊéïž
OpenJDK 21ãMaven 3.10ã
éĢéĐäļčåī
å ģč Issue
Refs #15700 ââ äŧ įŽŽ 4 éĄđïžå ķä―å ŦæĄåŧķååŧščŪŪįŧ§įŧäŋæåŧķåã