Skip to content

Assert failure: Popping a frame out of order in browser-wasm ReadyToRun runtime tests #133221

Open

Description

@lewing

Build Information

Build: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1582146
Build error leg or test failing: coreclr Pri0 Runtime Tests Run R2R_CG2 browser wasm checked — Regression_1 work item
Pull request: #133210

The Regression_1 work item in the browser-wasm ReadyToRun runtime-test leg asserts in Frame::Pop with frame-chain corruption. Helix job f484f658-1090-4625-adf3-8f35293caf50.

This is a different shape from #133219 (Object::Validate, GC-mode) and from the now-fixed #133220 (Thread::ObjectRefFlush). It is a frame-chain ordering assert, not a GC-mode assert, so it needs its own match block — Build Analysis matches on error text.

Helix categorises this as InfrastructureError. It is not — it is a real product assert in a checked runtime.

Error Details

   at EHTest.f1(Int64 arg0, Int64 arg1, Int64 arg2, Int32 arg3, Int64 arg4)
01:14:07.505 Failed test: JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b125091/b125091/b125091.dll
ASSERT FAILED
	Expression: pThread->GetFrame() == this && "Popping a frame out of order ?"
	Location:   /__w/1/s/src/coreclr/vm/frames.cpp:609
	Function:   Pop
	Process:    42
Frame (PrestubMethodFrame): 0x4fef1c
    Skipping 0x4fef1c

The failing test is JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b125091, an exception-handling test. The assert fires immediately after a System.AppDomainUnloadedException surfaces from EHTest.f1.

Context

Every remaining failure in this leg is an exception-handling test reaching an R2R delay-load pinvoke — b125091 here, NonVoidThrowHelper in JIT.opt (#133219), throwinfinally_50 in Methodical_d1, CctorThrowInlinedStatic in Loader. That is a better characterisation of the residue than the generic-dictionary-lookup theory in #132022, which #132420 already addressed.

Lead, not a fix

PopExplicitFrames (src/coreclr/vm/exceptionhandling.cpp:481-510) decides whether to pop or merely Reset() an active InlinedCallFrame when an exception unwinds through an inline pinvoke. It tests ExecutionManager::IsReadyToRunCode(m_pCallerReturnAddress), but wasm has no return address — JIT_PInvokeBeginImpl (src/coreclr/vm/wasm/helpers.cpp:708) stores the sentinel INLINED_PINVOKE_FROM_R2R (== 1, frames.h:792). IsReadyToRunCode((PCODE)1) finds no RangeSection and returns false, so the frame is Reset() but left linked on the thread frame chain. stackwalk.cpp:2335 already special-cases this exact sentinel under #ifdef TARGET_WASM; PopExplicitFrames has no equivalent. USE_PER_FRAME_PINVOKE_INIT is TARGET_64BIT-only, so wasm32 takes that branch.

That is a plausible cause of this assert specifically. It has not been validated against this test — a prototype was built and tested against JIT.opt/#133219, where it changed nothing, and was reverted rather than shipped unverified. Recording it so the next person does not have to re-derive it.

Error Message

{
  "ErrorMessage": [
    "Expression: pThread->GetFrame() == this && \"Popping a frame out of order ?\"",
    "Location:   /__w/1/s/src/coreclr/vm/frames.cpp:609",
    "Function:   Pop",
    "Process:    42"
  ],
  "ErrorPattern": "",
  "BuildRetry": false,
  "ExcludeConsoleLog": false
}

Why the match block is scoped this way

Array form: each element matches one line, in order, with arbitrary lines allowed between them.

"Popping a frame out of order ?" on its own is platform-neutral and would match this assert anywhere, on any architecture. The four elements together scope it: exact assert text, exact source location, asserting function, and Process: 42 — emscripten's fixed PID, which native platforms do not report. That last element keeps the match on browser-wasm.

All four elements were verified verbatim against the Regression_1 console log for Helix job f484f658-1090-4625-adf3-8f35293caf50.

Match-block verification

Every element was checked verbatim against the failing Helix console log for the job cited above.

The element set (Expression: / Location: / Function: / Process:) mirrors the block in #131925, which matched this same assert 277 times in a month — evidence that these lines are present in the artifact Build Analysis matches against. #131925 stopped matching because its block additionally pins object.cpp:514 (this build reports :506) and text specific to Loader/ContextualReflection, whose test has since been disabled on browser-wasm.

Outstanding: I have not yet observed a Build Analysis run attribute an occurrence to this issue, so the match is reasoned rather than demonstrated. If it does not pick up, the likely cause is that these merged-runner work items abort without publishing a test result for Build Analysis to attach to, in which case the fallback is a test-disable change rather than a KBE.

Note

This issue was authored with GitHub Copilot.

Known issue validation

Build: 🔎 https://dev.azure.com/dnceng-public/public/_build/results?buildId=1582146
Error message validated: [Expression: pThread->GetFrame() == this && "Popping a frame out of order ?" Location: /__w/1/s/src/coreclr/vm/frames.cpp:609 Function: Pop Process: 42]
Result validation: ❌ Known issue did not match with the provided build.
Validation performed at: 9/4/2026 3:40:40 AM UTC

Report

Build Repository Test Pull Request
1659118 dotnet/runtime OutOfProcessTest.Loader/binding/tracing/BinderTracingTest.ResolutionFlow/BinderTracingTest.ResolutionFlow.cmd #136163

Summary

24-Hour Hit Count 7-Day Hit Count 1-Month Count
0 0 1

Activity

  1. added
    arch-wasmWebAssembly architecture
    blocking-clean-ciBlocking PR or rolling runs of 'runtime' or 'runtime-extra-platforms'
    Known Build ErrorUse this to report build issues in the .NET Helix tab
    on Sep 4, 2026
  2. dotnet-policy-service commented on Sep 4, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
    See info in area-owners.md if you want to be subscribed.

  3. dotnet-policy-service commented on Sep 4, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @agocke
    See info in area-owners.md if you want to be subscribed.

  4. added
    Known Build ErrorUse this to report build issues in the .NET Helix tab
    blocking-clean-ciBlocking PR or rolling runs of 'runtime' or 'runtime-extra-platforms'
    and removed
    blocking-clean-ciBlocking PR or rolling runs of 'runtime' or 'runtime-extra-platforms'
    Known Build ErrorUse this to report build issues in the .NET Helix tab
    on Sep 4, 2026
  5. lewing commented on Sep 4, 2026

    @lewing
    MemberAuthor

    Status of the PopExplicitFrames lead recorded in the description — it is now better characterised, and it is the best remaining candidate for this issue specifically, but it is still untested against b125091.

    Verified twice by independent inspection: on wasm the predicate in PopExplicitFrames (exceptionhandling.cpp:481-510) is always false. ExecutionManager::IsReadyToRunCode(m_pCallerReturnAddress) is called with the INLINED_PINVOKE_FROM_R2R sentinel (== 1, frames.h:792) that JIT_PInvokeBeginImpl stores because wasm has no return address, and no RangeSection covers (PCODE)1. So the R2R InlinedCallFrame unlink is dead code on wasm, and the frame is Reset() but left linked on the thread frame chain — which is exactly the precondition for Frame::Pop asserting that it is popping out of order.

    Ruled out for #133219, not for this issue. During a JIT.opt repro of that other shape there were 25,854 entries to PopExplicitFrames with 0 active-InlinedCallFrame hits, and building the one-line fix left that assert unchanged. That is evidence it is not the cause there; it says nothing about here, because b125091 is an exception-handling test and this assert is a frame-chain ordering violation.

    Not yet tested against b125091 — the JIT/Regression subtree was not built in time. Next step for whoever picks this up: build that subtree, reproduce this assert, then apply the sentinel check to PopExplicitFrames mirroring the existing #ifdef TARGET_WASM special case at stackwalk.cpp:2335, and see whether it clears. USE_PER_FRAME_PINVOKE_INIT is TARGET_64BIT-only, so wasm32 definitely takes the affected branch.

    Also note the QCall-transition theory from #132022 has now been empirically refuted for the object.cpp:506 shape (details in #133219). It was never tested for this shape, so treat it as unknown here rather than inherited.

    Note

    This comment was authored with GitHub Copilot.

  6. added this to the 12.0.0 milestone on Sep 7, 2026
  7. removed
    untriagedNew issue has not been triaged by the area owner
    on Sep 7, 2026
  8. removed
    blocking-clean-ciBlocking PR or rolling runs of 'runtime' or 'runtime-extra-platforms'
    on Oct 12, 2026
  9. jeffschwMSFT commented on Oct 12, 2026

    @jeffschwMSFT
    Member

    removing blocking-clean-ci as it has not failed in 30 days

    24-Hour Hit Count 7-Day Hit Count 1-Month Count
    0 0 0
  10. lewing commented on Oct 27, 2026

    @lewing
    MemberAuthor

    Reproducibility analysis for this assert. I could not reproduce it, and I did not identify a cause or a fix.

    What failed: Build 1582146 ran the R2R_CG2 browser wasm checked leg on PR #133210's final head, f110d3585f (base 25f20c2fd36). That head already contains the long-local narrowing fix, so #133210 itself is not the fix. This is the only recorded occurrence I found. The R2R leg does not appear in the check runs of the PR's six earlier pushes, and a search of dotnet/runtime for the assert text finds only this issue (plus #133219, a different GC-mode assert, and an unrelated 2019 report).

    Local attempts (macOS arm64, browser-wasm Checked CoreCLR, libraries Release, Crossgen2 layout, RunCrossGen2=1, full JIT/Regression/Regression_1 wrapper, 107 tests passed in every run, no asserts, b125091 passed each time):

    Tree Result
    Exact failing tree: f110d3585f pass
    7e807becb8c (+ a local GC-mode patch) pass
    7e807becb8c with #133136 (SCC dispatcher fix) reverted pass
    main 14d606fc70a pass
    main with #133266 (IsManagedCode for wasm virtual IPs) reverted pass

    Reading of the log: the dump shows a single PrestubMethodFrame as the top frame, so Frame::Pop was called on a frame that is not on the chain. That fits a frame-chain bookkeeping problem. The PopExplicitFrames sentinel lead from the description is still untested against this assert.

    Not tested: other fixes that landed after the failing build and touch the same area were not isolated: #132965 (hand-written R2R-to-interpreter thunks removed), #133111 (framework R2R through the SDK pipeline), #133922 (prestub frame lifecycle). The CI build, logs and Helix job have expired, so I could not compare the Helix environment with my local one.

    Since then: the issue's hit counts are 0/0/0, but Build Analysis never matched this issue, so that is weak evidence. #133310 reports b125091 passing in the exact-base R2R baseline (#133308). #133305 and #133463 report it unsuppressed and passing in builds 1588226, 1588519 and 1589007, after #133463 restored the lane as non-blocking. I did not check results after mid-September.

    It looks like a one-off or an already-fixed failure rather than an open product bug, but I cannot prove either. Leaving the issue open for now. If someone wants to close it, a clean run of the R2R lane would back that up.

    Note

    This comment was drafted with GitHub Copilot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

  • Status
    No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions