You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Merge origin/develop into fix/bot-3115-upgrade-check-upgrade-guide-recommend-set-csrfencr
Reconciles this bot PR with #3123, which rewrote the 3x-to-4x upgrade
guide's CSRF-key section on develop after this branch was cut. Resolution:
- 3x-to-4x.mdx: develop's version wins wholesale — #3123 already covers
everything this branch changed (csrfCookieEncryptionSecretKey naming,
csrfStore="cookie" scoping, production-throws note, the #3115 caution
Aside, and the corrected Common-issues bullet), strictly superseding
this branch's three hunks.
- Module.cfc, UpgradeCheckCoverageSpec.cfc, upgrade.mdx, and the
changelog.d fragment auto-merged cleanly and remain this branch's
contribution (the upgrade-check rule fix itself is still absent from
develop).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
Copy file name to clipboardExpand all lines: .ai/wheels/troubleshooting/common-errors.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,6 +29,20 @@ Wheels failed to initialize. Check the server log for details.
29
29
30
30
**Note:** Before the #2774 fix, this failure cascaded into a second `[WO] does not exist` exception that hid the real cause. If you see the old cascade on a version that predates this fix (i.e. 4.0.1 or earlier), the underlying cause is always a failed `onApplicationStart` — see above.
31
31
32
+
### "key [ENGINEADAPTER] doesn't exist" / "Element WHEELS.ENGINEADAPTER is undefined" in dev error page
33
+
**Error (on-page or in server log):**
34
+
```
35
+
key [ENGINEADAPTER] doesn't exist (Lucee)
36
+
Element WHEELS.ENGINEADAPTER is undefined (Adobe CF)
37
+
```
38
+
39
+
**Cause:** An exception during `onApplicationStart` (e.g. `Wheels.Cors.InvalidConfiguration` from an invalid `config/settings.cfm` value) triggered `onError`, which itself crashed because three request-lifecycle helpers — `$getRequestTimeout()`, `$statusCode()`, and `$contentType()` — read `application.wheels.engineAdapter` directly after gating on `$hasEngineAdapter()`. That gate checks both `application.wheels` and the startup-staging struct `application.$wheels`, but the subsequent read assumed the adapter had been promoted to `application.wheels`. When only the `$wheels` branch matched (the failed-startup state), the read threw and replaced the original exception (fixed in [#3108](https://github.com/wheels-dev/wheels/pull/3108)).
40
+
41
+
**Resolution:**
42
+
The `[ENGINEADAPTER]` crash is a symptom — the real error happened during startup. Check the server log for the original `onApplicationStart` exception; common causes include invalid middleware configuration, a missing CFML mapping, or a syntax error in `config/settings.cfm` or `config/routes.cfm`.
43
+
44
+
After upgrading past the #3108 fix, `onError` surfaces the original startup exception directly.
Copy file name to clipboardExpand all lines: CLAUDE.md
+15-12Lines changed: 15 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -107,6 +107,8 @@ Model finders return query objects, not arrays. Loop accordingly.
107
107
.end()
108
108
```
109
109
110
+
`scope()`, `namespace()`, `package()`, and `controller()` also accept `callback=` and auto-close the scope when the callback returns — use the same callback form for these too (#3072).
111
+
110
112
### 4. HTML5 Form Helpers Exist — Use Them
111
113
```cfm
112
114
#emailField(objectName="user", property="email")#
@@ -259,7 +261,7 @@ For new migrator helpers or anywhere you accept a column-name argument: declare
259
261
component extends="Model" {
260
262
function config() {
261
263
// Table/key (only if non-conventional)
262
-
table("tbl_users"); // setter is table(); tableName() is a getter — tableName("x") is a silent no-op (#3079)
264
+
table("tbl_users"); // setter is table(); tableName() is a getter — tableName("x") throws Wheels.InvalidArgument in dev/testing, no-op in production (#3079)
263
265
setPrimaryKey("userId");
264
266
265
267
// Associations — all named params when using options
@@ -349,8 +351,9 @@ Resolves `params.key` into a model instance before the action runs. Lands in `pa
@@ -713,7 +716,7 @@ User-facing `fix`/`feat` PRs add a **fragment file**, never a direct `CHANGELOG.
713
716
714
717
There is no `wheels mcp setup` command — copy the JSON above into `.mcp.json` manually (see the MCP integration guide for OpenCode/Cursor variants).
715
718
716
-
Tools are auto-discovered from `cli/lucli/Module.cfc` public functions, prefixed with the module name (`wheels_generate`, `wheels_migrate`, `wheels_test`, `wheels_reload`, `wheels_seed`, `wheels_analyze`, `wheels_validate`, `wheels_routes`, `wheels_info`, `wheels_destroy`, `wheels_doctor`, `wheels_stats`, `wheels_notes`, `wheels_db`, `wheels_upgrade`, `wheels_create`, `wheels_deploy`, `wheels_packages`). CLI-only tools (`main`, `mcp`, `d`, `g`, `new`, `console`, `start`, `stop`, `browser`) are hidden via `mcpHiddenTools()`.
719
+
Tools are auto-discovered from `cli/lucli/Module.cfc` public functions. Names in `tools/list` are the bare function names — NOT `wheels_*`-prefixed (live-verified on the released 4.0.3 CLI): `analyze`, `create`, `db`, `deploy`, `destroy`, `doctor`, `generate`, `info`, `migrate`, `notes`, `packages`, `reload`, `routes`, `seed`, `stats`, `test`, `upgrade`, `validate` (18 tools; the `wheels` server entry in `.mcp.json` namespaces them per client). CLI-only tools (`main`, `mcp`, `d`, `g`, `new`, `console`, `start`, `stop`, `browser`) are hidden via `mcpHiddenTools()`.
717
720
718
721
**Deprecated:** the in-dev-server HTTP endpoint at `/wheels/mcp`. Emits a deprecation notice on first request. Migrate to the stdio surface.
719
722
@@ -725,13 +728,13 @@ Prefer MCP tools when the Wheels MCP server is available. Fall back to CLI other
725
728
726
729
| Task | MCP | CLI |
727
730
|------|-----|-----|
728
-
| Generate |`wheels_generate(type, name, attributes)`|`wheels g model/controller/scaffold Name attrs`|
- A compile-time or runtime error in a `config/*.cfm` file loaded during application start (e.g. an Adobe CF-illegal top-level `var di = injector();` in `config/services.cfm`) is now caught by `$includeConfig()`, logged with the offending file name, and skipped — instead of cascading out of `onApplicationStart` into a masked, application-wide HTTP 500 whose secondary `onError` failure hid the real cause (#3063)
1
+
- A compile-time or runtime error in a `config/*.cfm` file loaded during application start (e.g. an Adobe CF-illegal top-level `var di = injector();` in `config/services.cfm`) is now logged and rethrown by `$includeConfig()` as a named `Wheels.ConfigIncludeFailed` error carrying the failing template path and the original engine message (original type/detail preserved in `detail`) — a clear, located, fail-closed boot error in every environment, instead of the old masked, application-wide HTTP 500 whose secondary `onError` failure hid the real cause. An app whose config did not load no longer boots fail-open on framework defaults (#3063)
- Router: `scope()` (and the `namespace()` / `package()` / `controller()` wrappers that forward to it) silently dropped a `callback=` argument — the callback never ran (its routes 404'd as `Wheels.RouteNotFound`) and nothing closed the scope, so every route declared afterward (including `resources`, `root`, and `wildcard`) inherited the scope's path prefix and middleware. `scope()` now consumes `callback` the same way `group()` does: it runs `callback(this)` to declare the nested routes and auto-closes the scope, so the documented `.scope(path="/api", callback=function(map){ ... })` and `.namespace(name="admin", callback=...)` forms work as written and routes after the block are no longer swallowed (#3072)
- The action-dispatch gate now returns HTTP 404 (not 500) when a request targets a framework helper or `$`-prefixed internal method, matching the missing-action behavior promised by #2845 and CLAUDE.md Anti-Pattern 8. `$callAction` routes the block through `$throwErrorOrShow404Page` (committing the 404 header and rendering the production 404 page), and the `EventMethods` status map now treats `Wheels.ActionNotAllowed` as a 404 alongside the `Wheels.*NotFound` family (#3075)
- The development error page no longer masks app-start exceptions: `$getRequestTimeout()`, `$statusCode()`, and `$contentType()` now resolve the engine adapter via `$engineAdapter()` (which checks both `application.wheels` and the `application.$wheels` startup-staging struct) instead of reading `application.wheels.engineAdapter` unconditionally. Previously, when a throw during settings/routes load left the adapter only in `application.$wheels`, `onError` itself crashed with `key [ENGINEADAPTER] doesn't exist` (Lucee) / `Element WHEELS.ENGINEADAPTER is undefined` (Adobe), hiding the original failure such as the `Wheels.Cors.InvalidConfiguration` guard (#3076)
-`sendFile()`: an absolute `directory` argument is now used verbatim instead of being post-processed, honouring the documented "must be a full path … outside of the web root" contract. Previously the resolver ran the caller-supplied absolute path through the `/wheels` mapping rewrite — substring-hijacking any directory containing `/wheels` (e.g. `/var/www/wheels/uploads`) on every engine — and through an `ExpandPath()` fallback that web-root-prefixed the path on Adobe CF, so serving files from outside the web root threw `Wheels.FileNotFound` there. The verbatim branch only engages when the directory actually exists on disk, so the long-standing webroot-relative idiom (`directory="/reports/"`) keeps resolving against the web root via `ExpandPath()`. Relative `filePath`-based resolution and the `..`-traversal guard are unchanged (#3077)
- Calling `tableName()` with an argument now throws `Wheels.InvalidArgument` when error information is shown (development/testing) instead of silently no-opping — some 4.0-era docs mistakenly taught `tableName("x")` as the table setter, leaving models on their convention table. The zero-argument getter is unchanged, production stays a no-op, and the canonical setter remains `table()` (#3079)
- CLI: migration failures now reach the exit code. A failed `up()`/`down()` step that `migrateTo()` folds into its output (e.g. `Error migrating to <version>.`) made `wheels migrate latest|up|down` print the error inside the green success block and still exit 0; `wheels db reset --force` exited 0 when it refused on a `ServerNotRunning` check; and `wheels migrate forget|pretend` refusals (not in tracking table, matching local file exists, already applied, no matching file) printed red but exited 0. All three now exit non-zero, so a `wheels migrate latest && …` CI gate no longer proceeds as if the schema moved. Informational dry-run output (missing `<version>` / missing `--yes`) still exits 0. The migrate-side sibling of the #2973/#2987 seeder honesty fix (#3081)
-`wheels seed --generate` now actually creates rows and reports honest success/failure. The CLI seed bridge's generate loop iterated `$classData().properties` (a struct keyed by property name) as if it were an array of property structs — so `prop.name` threw `there is no property with name [NAME] found in [string]`, every model errored, zero rows were created, yet the run still returned `success=true` and the CLI printed "Seeding completed." with exit 0. The generate path is now a dedicated, unit-tested `wheels.Seeder.generateSeeds(models, count)` method that iterates the property struct correctly and forces overall `success=false` when any model fails or no rows are created, so the CLI surfaces a non-zero exit instead of silently lying (the #2987 honesty fix had only covered convention mode) (#3082)
0 commit comments