Skip to content

fix(public): defer helper promote until after Public.$init - #3426

Closed
bpamiri wants to merge 1 commit into
developfrom
cursor/public-init-emptystack-0a8a
Closed

fix(public): defer helper promote until after Public.$init#3426
bpamiri wants to merge 1 commit into
developfrom
cursor/public-init-emptystack-0a8a

Conversation

@bpamiri

@bpamiri bpamiri commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adobe CF 2023 + CommandBox throws EmptyStackException from NeoPageContext.popSuperScope on the first request after a cold start. The stack lands on vendor/wheels/events/onapplicationstart.cfc:409 ($createObjectFromRootPublic.$init). Discarding the 4.0.6 files and going back to 4.0.5 clears it. A later page load that compiles a second time also succeeds.

This PR is that first-boot EmptyStack path only. It is not the torn-down application.wo / onError String[] report on issue 3379. No onError, EventHandlerInterface, or onSessionEnd edits.

Related to #3379.

Root cause (confirmed)

The desk hypothesis holds. I compared the three trees rather than taking the write-up as given.

  1. Field stack from the 4.0.6 CommandBox report is onapplicationstart.cfc:409$createObjectFromRoot(path="wheels", fileName="Public", method="$init"). Adobe then pops a super-scope inside UDFMethod.invoke (NeoPageContext.popSuperScope) and the stack is empty.
  2. That call site already existed in 4.0.5 (line 419). It is not new work.
  3. The 4.0.6 delta is inside Public.$init (commit 6bff05441f / compat-matrix: engine-job failures are invisible (continue-on-error) — harden after burning down pre-existing leg debt #3302): after include "/wheels/public/helpers.cfm" it now calls $scanAndPromoteIncludedGlobals(). That helper is a parent-class method on wheels.Global. 4.0.5 $init is include + return only.
  4. Same-method include + parent-method promote nests Adobe's include page-context with a super-scope push. On the first compile of helpers.cfm after a CommandBox cold start, popSuperScope runs against an empty stack. A later request succeeds because the include is already compiled, matching the report.
  5. The promote itself is still required. Lucee 6 / Adobe 2023 / Adobe 2025 do not lift include UDFs onto this. The memoized $promoteIncludedGlobalsToThis() wrapper cannot be used here: Global's pseudo-constructor already cached a pre-include key list for wheels.Public.

Hoisting the include into a helper and still calling the scan from $init is the other obvious shape. I discarded it. 4.0.5 $init is include-and-return and does not throw. If the inherited scan extra-pops, $init's own return still EmptyStacks. The safe contract is the one that already worked: $init does not call the scan. Promote after $createObjectFromRoot returns, once that Invoke has fully unwound.

4.0.5 vs 4.0.6 vs develop

Tree SHA Public.$init
4.0.5 tag 51eb6e47 include "/wheels/public/helpers.cfm" then return this
4.0.6 tag 5928131d same include, then $scanAndPromoteIncludedGlobals(), then return
develop (this base) c64f6a9b identical to 4.0.6

$createObjectFromRoot(..., Public, $init): 4.0.5 line 419, 4.0.6 and develop line 409. Same call.

What changed

  • Public.$init is include-and-return again (the 4.0.5 shape). It does not call $scanAndPromoteIncludedGlobals().
  • onapplicationstart.cfc calls $scanAndPromoteIncludedGlobals() on the Public instance after $createObjectFromRoot returns, so the compat-matrix: engine-job failures are invisible (continue-on-error) — harden after burning down pre-existing leg debt #3302 helpers-on-this surface still applies once the include nest has unwound.
  • The debug-IP Public create in the shipped Application.cfc copies does the same two-step. Those paths replace application.wheels.public after $init; without the follow-up scan they would drop helpers on Lucee 6 / Adobe. onSessionEnd is untouched.

Tests

Adobe CF 2023 + CommandBox first-boot cannot be fully simulated here. The spec pins the contract that prevents the nest.

Out of scope

Left on issue 3379, not this PR:

  • Torn-down application.wo during onError (Element wo is undefined in a Java object of type class [Ljava.lang.String;)
  • EventHandlerInterface missing-template during $invoke from onError
  • Copying the v4.0.6 Application.cfc (that drop would remove the develop onSessionEnd guard)

Type of Change

  • Bug fix
  • DCO sign-off
  • Tests
  • Changelog fragment (changelog.d/3379-first-boot-emptystack.fixed.md)

Test Plan

  1. wheels test --core --ci --filter=events (new spec)
  2. wheels test --core --ci (full framework suite)
  3. Field check (Adobe CF 2023 + CommandBox): stop the server, start cold, first GET must not EmptyStack; Public GUI helpers still resolve after init.
Open in Web Open in Cursor 

Adobe CF 2023 + CommandBox first cold start threw EmptyStackException at
onapplicationstart.cfc $createObjectFromRoot. 4.0.6 ran
$scanAndPromoteIncludedGlobals inside Public.$init after the helpers
include. Restore the 4.0.5 include-and-return $init and promote after
$createObjectFromRoot returns so the 3302 helpers-on-this surface still
applies once the include nest has unwound.

Refs #3379

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Peter Amiri <peter@alurium.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants