Skip to content

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

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions