Skip to content

TESTBOX-454: Fix KeyNotFoundException [url] crashing every CLI run - #202

Merged
lmajano merged 1 commit into
developmentfrom
fix/cli-url-scope-guard
Aug 29, 2026
Merged

TESTBOX-454: Fix KeyNotFoundException [url] crashing every CLI run#202
lmajano merged 1 commit into
developmentfrom
fix/cli-url-scope-guard

Conversation

@lmajano

@lmajano lmajano commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Description

Every ./testbox/run CLI invocation (e.g. ./testbox/run --stream --verbose) crashes on BoxLang v1.17.0+58 with:

[ERROR] [EXCEPTION] The requested key [url] was not located in any scope or it's undefined
ortus.boxlang.runtime.types.exceptions.KeyNotFoundException: ...
	at ortus.boxlang.runtime.context.ScriptingRequestBoxContext.scopeFind(ScriptingRequestBoxContext.java:272)
	at ortus.boxlang.runtime.context.FunctionBoxContext.scopeFindNearby(FunctionBoxContext.java:395)
	at boxgenerated.boxclass.testbox.system.Testbox$cfc.invokeFunction_runRaw(TestBox.cfc:408)
	at boxgenerated.boxclass.testbox.system.Testbox$cfc.invokeFunction_run(TestBox.cfc:351)
	at ...Boxlangrunner$bx.invokeFunction_main(BoxLangRunner.bx:233)

Discovered right after TESTBOX-453/#201 shipped: that fix cleared an earlier crash that always happened before execution reached this code, masking this one. Now that runs get further, this is hit on every single CLI invocation, deterministically.

Root cause: TestBox.cfc's runRaw() (~line 408) and dryRun() (~line 527) unconditionally reference the url scope, to support web-request query-string test filters (?testBundles=, ?testSuites=, etc.):

if ( structKeyExists( url, "testBundles" ) && !isNull( url.testBundles ) ) {
    testBundles.append( listToArray( urlDecode( url.testBundles ) ), true );
}

The url scope only exists in a real HTTP request context — it is never registered at all when TestBox runs via the BoxLang CLI. A prior fix (#200, "support full null runtimes") added the structKeyExists( url, "testBundles" ) guard, but that doesn't actually help: resolving the bare url identifier itself is what throws (BoxLang has to look the scope up before it can even pass it as an argument to structKeyExists()) — the key-existence check never gets a chance to run.

Fix: skip these URL-based filter blocks entirely when variables.IS_CLI is true (already computed at class init). CLI users already have --filter-bundles/--filter-suites/--filter-specs via BoxLangRunner.bx for the same purpose, so nothing is lost.

I installed BoxLang v1.17.0+58 locally, reproduced the exact crash against the real merged development branch (structKeyExists guard included) using the maintainer's exact command, then confirmed 4/4 clean runs with the same command after the fix, plus --dry-run and --stream (which exercise both patched call sites).

Jira Issues

https://ortussolutions.atlassian.net/browse/TESTBOX-454 (related: TESTBOX-453 / #201)

Type of change

  • Bug Fix

Checklist

  • My code follows the style guidelines of this project cfformat
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Note: same as #201, this fix touches the CLI runner path, which isn't exercised by TestBox's existing test suite (that runs against CFML engine web servers, not the BoxLang CLI entry point) — verification here was a manual local repro against the actual BoxLang engine build, described above and in the linked Jira issue.


Generated by Claude Code

Reported by the maintainer running ./testbox/run --stream --verbose
against BoxLang v1.17.0+58: every CLI run crashed immediately after
the recent CLI-runner fix (#201) cleared the way to reach this code,
with:

  KeyNotFoundException: The requested key [url] was not located in
  any scope or it's undefined
    at ...ScriptingRequestBoxContext.scopeFind
    at ...Testbox$cfc.invokeFunction_runRaw(TestBox.cfc:408)

Root cause: runRaw() and dryRun() unconditionally reference the `url`
scope to support web-request query-string test filters (?testBundles=,
?testSuites=, etc.). The `url` scope only exists in a real HTTP
request context - it is never registered at all when TestBox runs via
the BoxLang CLI. #200 ("support full null runtimes") added a
`structKeyExists( url, "testBundles" )` guard, but that doesn't help
here: resolving the bare `url` identifier itself is what throws -
structKeyExists() never gets a chance to run, since BoxLang has to
look up `url` as a scope before it can pass it as an argument.

Fix: skip these URL-based filter blocks entirely when
variables.IS_CLI is true (already computed at class init). CLI users
already have --filter-bundles/--filter-suites/--filter-specs for the
same purpose via BoxLangRunner.bx, so nothing is lost.

Verified locally against BoxLang v1.17.0+58: reproduced the exact
reported crash against the real merged development branch first
(structKeyExists guard included), then confirmed the fix with the
maintainer's exact command (./testbox/run --streamingj --verbose,
4/4 clean runs), plus --dry-run and --stream, which exercise both
patched call sites (runRaw() and dryRun()).
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

lucee@6 21 fullNull=false Test Results

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

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

boxlang@be 21 fullNull=false Test Results

  1 files  ±0   30 suites  ±0   9s ⏱️ ±0s
737 tests ±0  716 ✅ ±0  21 💤 ±0  0 ❌ ±0 
754 runs  ±0  722 ✅ ±0  32 💤 ±0  0 ❌ ±0 

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

boxlang@1 21 fullNull=false Test Results

  1 files  ±0   30 suites  ±0   9s ⏱️ ±0s
737 tests ±0  716 ✅ ±0  21 💤 ±0  0 ❌ ±0 
754 runs  ±0  722 ✅ ±0  32 💤 ±0  0 ❌ ±0 

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

lucee@7 21 fullNull=false Test Results

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

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

boxlang-cfml@1 21 fullNull=false Test Results

  1 files  ±0   30 suites  ±0   8s ⏱️ ±0s
738 tests ±0  717 ✅ ±0  21 💤 ±0  0 ❌ ±0 
754 runs  ±0  723 ✅ ±0  31 💤 ±0  0 ❌ ±0 

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

adobe@2023 21 fullNull=false Test Results

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

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

adobe@2023 21 fullNull=true Test Results

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

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

adobe@2025 21 fullNull=false Test Results

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

Results for commit 934217b. ± Comparison against base commit 3499a24.

♻️ This comment has been updated with latest results.

@lmajano
lmajano merged commit c69fb9b into development Aug 29, 2026
27 checks passed
lmajano added a commit that referenced this pull request Aug 29, 2026
)

#202 guarded the URL-based test filter blocks in runRaw()/dryRun()
with plain `!variables.IS_CLI`, which assumes the url scope is never
present in CLI mode. That's true today, but the guard shouldn't bake
in that assumption - it should check reality instead of the engine
mode. If a CLI context is ever running with a url scope registered
(a future BoxLang change, an embedding runner, etc.), skipping unconditionally
on IS_CLI would silently drop those overrides for no reason.

Confirmed isDefined( "url" ) is the safe way to probe for this: unlike
referencing the bare `url` identifier (which throws when the scope
isn't registered), isDefined() on a scope name returns false without
error.

New guard: `!variables.IS_CLI || isDefined( "url" )` - run the block
whenever we're not in CLI mode (unchanged, existing behavior) OR the
url scope actually exists (covers any CLI context where it does).
Only skip when neither holds, i.e. CLI with no url scope - which is
where the original #202 crash happened.

Verified locally against BoxLang v1.17.0+58: confirmed isDefined("url")
returns false without throwing where a bare `url` reference does
throw, then re-ran the full local repro (./testbox/run --streamingj
--verbose, --stream, --dry-run) with the expanded guard - all clean,
same as after #202.

Co-authored-by: Claude <noreply@anthropic.com>
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