Skip to content

Reload: bare ?reload=true does NOT re-include app/global/*.cfm files — full re-init requires ?reload=true&password=<admin> #2792

@bpamiri

Description

@bpamiri

Summary

Hitting a URL with ?reload=true does NOT re-evaluate <cfinclude> directives in app/global/functions.cfm (or any included file like app/global/datapai/helpers.cfm). New functions added to those included files are invisible until a full applicationStop() runs — which is only triggered by ?reload=true&password=<admin> (where <admin> is the admin.password value from lucee.json).

Net effect: when developing new helper functions, the obvious developer reflex (?reload=true) lies. The page renders without error, but the new helper is still undefined. Looks like Lucee/Wheels just isn't picking it up, but actually the reload silently skipped the re-include step.

Repro

  1. Add a new function to a project's global helpers:
// app/global/datapai/helpers.cfm (the file is included from app/global/functions.cfm)
<cfscript>
function newHelperJustAdded() {
    return "I am brand new";
}
</cfscript>
  1. Hit the dev server with ?reload=true:
curl -s "http://localhost:60050/?reload=true" -o /dev/null -w "HTTP %{http_code}\n"
# HTTP 302
  1. Try to use the new helper in a controller or view:
// app/controllers/Smoke.cfc
function index() {
    writeOutput(newHelperJustAdded());  // throws: function 'newHelperJustAdded' is not defined
    abort;
}
  1. Now hit with ?reload=true&password=titan4dev (or whatever your Lucee admin password is, from lucee.json):
curl -s "http://localhost:60050/?reload=true&password=titan4dev" -o /dev/null -w "HTTP %{http_code}\n"
# HTTP 302
  1. Re-try step 3 — the helper now resolves correctly.

The difference: ?reload=true&password=... triggers applicationStop(), forcing the application scope to be torn down and onApplicationStart to re-fire, which re-evaluates the app/global/functions.cfm include chain. Bare ?reload=true runs a softer reload that re-loads SOME settings/routes but doesn't re-include global function files.

Impact

This caused real iteration friction during the DataPAI Phase 0 build. Every time we added a new helper function and tried to test it, the first ?reload=true request would appear successful but the function was missing. Spending several minutes debugging "why isn't my helper working" before remembering to use the password form is a common pattern.

Documentation in the dev guides mentions ?reload=true as the standard reload mechanism but doesn't flag this limitation. Users discover it through frustration.

Why this happens (my hypothesis)

Wheels' ?reload=true handling (in vendor/wheels/events/onapplicationstart.cfc and the request lifecycle) re-runs a subset of init code — it re-loads application.wheels config, re-fires routes.cfm to rebuild the route table, etc. But it does NOT re-evaluate app/global/functions.cfm, which is <cfinclude>'d into Global.cfc at the time Global.cfc itself was first instantiated. Including a CFM file at component construction time is a one-shot operation; the included symbols are merged into the component's variables scope and stay there until the component is re-instantiated.

A ?reload=true doesn't tear down application.wo (the Global.cfc instance), so the included symbols remain stale.

?reload=true&password=<admin> calls Lucee's applicationStop() (gated by the admin password as a safety mechanism). This tears down the application scope completely, forcing onApplicationStart to re-fire which re-instantiates application.wo (Global.cfc) which re-evaluates the <cfinclude> chain.

Suggested fixes (any of)

  1. Make ?reload=true actually re-include app/global/*.cfm by either (a) re-instantiating Global.cfc as part of the reload pipeline, or (b) tracking the included files and re-evaluating them explicitly. Either way, the behavior matches the developer's mental model: "reload means everything in the app is fresh".

  2. Document the limitation prominently in guides.wheels.dev/v4-0-0/configuration-and-defaults/. Show side-by-side what ?reload=true does vs. ?reload=true&password=<admin> so users know which one to reach for.

  3. Print a warning during dev runs when a request notices the app/global/*.cfm files have a newer mtime than the application's load time. Something like "Your app/global files have changed since startup; run ?reload=true&password=<admin> for a full reload." Only in dev, only once per file-change-detection.

  4. Add a wheels reload CLI command that hits the password-reload URL with the password from lucee.json. Skips the muscle-memory issue entirely.

Repo / version

  • Wheels Core: 4.0.0-SNAPSHOT+1779
  • File: vendor/wheels/events/onapplicationstart.cfc (where the reload-with-password branching lives — line ~417 is the $location redirect post-reload, but the reload-decision logic is earlier in the file)

Where found

Surfaced repeatedly during the DataPAI Phase 0 build in Titan. We added ~10 helpers across 2 helper-include files; each helper's first invocation required learning (or remembering) to use the password reload. The CLAUDE.md gotcha list captured at the end of Phase 0 (paiindustries/titan PR #3337 commit e3ab806bb) flags this as one of 9 things to know about Wheels' dev iteration model.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions