Skip to content

Fix functions.bxs/layout.bxm include failures via registered mappings - #126

Closed
lmajano wants to merge 3 commits into
developmentfrom
claude/dogfood-docs-help-27ejhn
Closed

Fix functions.bxs/layout.bxm include failures via registered mappings#126
lmajano wants to merge 3 commits into
developmentfrom
claude/dogfood-docs-help-27ejhn

Conversation

@lmajano

@lmajano lmajano commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

PR #125 fixed pages.yml's deploy trigger, but the very next Publish Docs run (triggered automatically by that merge) still failed to deploy - 3-4 of the 10 theme matrix jobs crashed each time, with a different subset of themes failing on each run:

Error: functions.bxs at [/home/runner/work/bx-sites/bx-sites/docs/functions.bxs] failed to load: The template path [/home/runner/work/bx-sites/bx-sites/docs/functions.bxs] could not be found.

assemble-and-deploy has needs: build, so any matrix job failing skips the deploy entirely - this is the reason the site still wasn't publishing after #125.

Root cause

BoxLang's include statement never resolves a bare OS absolute filesystem path against the real filesystem - an absolute-looking path is coerced against registered mappings/webroot instead. FunctionsLoader.bx's load() called include functionsPath with a real, fileExists()-confirmed absolute path, which is simply an unsupported way to reach an arbitrary, runtime-computed directory - not a timing race, as an earlier version of this PR assumed. It went unnoticed until now because this repo only recently started shipping a real, non-empty docs/functions.bxs.

ThemeRenderer.bx's include arguments.themeDir & "/layout.bxm" has the identical pattern - it just hasn't been observed failing yet, since every failing build so far aborted at functionsLoader.load() (the very first include the whole build performs) before any page ever reached that code path.

Fix

Both now call getBoxRuntime().getConfiguration().registerMapping() to register a mapping for the target directory, then include through that mapping-prefixed path instead - the actual supported mechanism for including a runtime-computed absolute path. Any other failure (e.g. a real syntax error in a project's own functions.bxs) still throws immediately, unchanged from before.

Test plan

  • Existing FunctionsLoaderSpec.bx/ThemeRendererSpec.bx suites still pass
  • After merge, confirm the next Publish Docs run has all 10 theme builds succeed and assemble-and-deploy actually runs
  • Confirm bxsites.io serves the versioned site (1.0.x at the root, /next/ for work in progress)

Roughly a third of the "Publish Docs" theme-gallery matrix jobs (fresh
JVM per job) were hitting functions.bxs at [...] failed to load: The
template path [...] could not be found on the very first include the
whole build performs, immediately after fileExists() on that exact
path had just confirmed it was there - blocking assemble-and-deploy
(needs: build) every time, since a different subset of themes crashed
on each run.

That shape - transient, only on the first include of the run, on a
path a moment-old existence check already verified - points to a
cold-start race in BoxLang's own template resolver rather than a real
missing file. load() now retries up to twice more (150ms/300ms
backoff) when it hits that specific message and a fresh fileExists()
recheck still confirms the file exists; any other failure, including a
real syntax error in a project's own functions.bxs, still throws
immediately on the first attempt.
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

Test Results

0 tests  ±0   0 ✅ ±0   0s ⏱️ ±0s
0 suites ±0   0 💤 ±0 
0 files   ±0   0 ❌ ±0 

Results for commit 90cd93c. ± Comparison against base commit d7b975f.

♻️ This comment has been updated with latest results.

The previous commit treated the symptom as a timing race and retried
the include. It isn't a race: BoxLang's include statement never
resolves a bare OS absolute path against the filesystem at all - an
absolute-looking path gets coerced against registered
mappings/webroot instead, so include functionsPath (and
ThemeRenderer.bx's identical include arguments.themeDir &
"/layout.bxm") were simply an unsupported way to reach an arbitrary,
runtime-computed directory, regardless of how many times it's retried.

Both now call getBoxRuntime().getConfiguration().registerMapping() to
register a mapping for the target directory, then include through that
mapping-prefixed path - the actual supported mechanism for this. Fixed
ThemeRenderer.bx's layout.bxm include too, even though it hasn't been
observed failing yet - it's the identical pattern, and every failing
build so far aborted at functionsLoader.load() before any page ever
reached this code path, so absence of evidence there isn't evidence of
absence.

lmajano commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

Update: the "cold-start race" theory in this PR's description was wrong. Per @lmajano: BoxLang's include statement never resolves a bare OS absolute path against the real filesystem - an absolute-looking path gets coerced against registered mappings/webroot instead. include functionsPath (and the identical pattern in ThemeRenderer.bx's include arguments.themeDir & "/layout.bxm", which runs on every page render) were simply an unsupported way to reach an arbitrary, runtime-computed directory - no amount of retrying would have reliably fixed it.

Pushed a follow-up commit that replaces the retry loop with the actual fix: both now call getBoxRuntime().getConfiguration().registerMapping() to register a mapping for the target directory, then include through that mapping-prefixed path instead. Fixed ThemeRenderer.bx too even though it hasn't been observed failing yet in CI - every failing build so far aborted at functionsLoader.load() (the very first include in build()) before any page ever reached that code path, so absence of evidence there isn't evidence of absence.


Generated by Claude Code

@lmajano lmajano changed the title Retry FunctionsLoader's include on a transient template-not-found race Fix functions.bxs/layout.bxm include failures via registered mappings Aug 29, 2026

lmajano commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

tests / Tests bx-sites failed again on the latest commit (c8260770) - same known flake as before (ClassNotFoundBoxLangException on TestBox's own BoxLangRunner.bx, same engine build v1.17.0+58), before any test code runs. Unrelated to this PR's registerMapping change. Same 403 blocking me from re-running it myself. Keeping this PR watched.


Generated by Claude Code

tests.yml's test suite was intermittently failing before a single spec
ran, with ClassNotFoundBoxLangException on TestBox's own
BoxLangRunner.bx - blocking this PR's own CI (and any other PR's)
independently of the registerMapping fix above.

Root cause turned out to be a real TestBox bug, not a BoxLang engine
issue: server.cli.parsed.positionals leaks the runner's own invocation
path as argv[0] on current engine builds, and BoxLangRunner.bx's
naive `positional[1]` grab misread that as a user-supplied bundle
argument whenever none was actually given. Fixed upstream in
Ortus-Solutions/TestBox#201 (reproduced and verified locally there).

Pin id=testbox to id=testbox@be so CI picks up that fix from the
bleeding-edge channel now, rather than waiting on a tagged stable
TestBox release. Revert to the unpinned id=testbox once one ships it.

lmajano commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

Update on the recurring tests / Tests bx-sites flake: turned out to be a real bug in TestBox itself, not the BoxLang engine. Root-caused and fixed in Ortus-Solutions/TestBox#201 - server.cli.parsed.positionals leaks the runner's own invocation path as argv[0] on current engine builds, and BoxLangRunner.bx misread that as a user-supplied bundle argument.

Pushed a commit pinning tests.yml's box install id=testbox to id=testbox@be so this PR's own CI (and every other PR's) picks up that fix now instead of waiting on a tagged TestBox release. Watching for CI to actually go green this time.


Generated by Claude Code

lmajano commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

Progress, but not there yet: testbox@be (7.1.0-snapshot) does fix the original ClassNotFoundBoxLangException - that's gone. But it exposes a second, previously-masked TestBox bug: TestBox.cfc's runRaw() unconditionally reads url.testBundles/url.testSuites/url.testSpecs (~line 408), and the url scope doesn't exist in CLI execution - only in a real HTTP request. This was always broken for CLI runs, just never reached before because the argv[0]-leak bug always crashed earlier in execution. Now that that's fixed, every single ./testbox/run invocation hits this deterministically:

ortus.boxlang.runtime.types.exceptions.KeyNotFoundException: The requested key [url] was not located in any scope or it's undefined
	at ...
	at boxgenerated.boxclass.testbox.system.Testbox$cfc.invokeFunction_runRaw(.../testbox/system/TestBox.cfc:408)

Sent this back to the same TestBox investigation thread (same repo, same context) to fix. Keeping this PR watched until it's resolved.


Generated by Claude Code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants