Skip to content

fix(velocity): fail loud when global macro library load fails (#35601)#35768

Merged
dsolistorres merged 5 commits into
mainfrom
issue-35601-velocity-fail-loud
May 22, 2026
Merged

fix(velocity): fail loud when global macro library load fails (#35601)#35768
dsolistorres merged 5 commits into
mainfrom
issue-35601-velocity-fail-loud

Conversation

@dsolistorres
Copy link
Copy Markdown
Member

@dsolistorres dsolistorres commented May 20, 2026

Summary

Closes #35601 (follow-up implementation from spike #35329).

VelocimacroFactory silently swallowed ResourceNotFoundException when loading the configured velocimacro.library files at engine init, allowing engine.init() to return successfully with no global macros registered. After K8s pod restarts with transient I/O errors (notably on EFS-backed volumes), #renderMarks($element) and #editContentlet rendered as literal text on every public page until the JVM was restarted.

Changes

  • VelocimacroFactory.initVelocimacro() — track loaded/failed libraries through the load loop. The ResourceNotFoundException catch now logs at ERROR with the failed library file name. After the loop, an INFO summary line lists loaded and failed libraries (greppable for ops). When any library failed, throw VelocityException with an actionable message naming the failed library and the opt-out flag.
  • New flag velocimacro.library.fail-on-missing (default true) preserves the legacy silent-warn behavior for self-hosted operators who intentionally configure optional libraries.
  • RuntimeConstants.VM_LIBRARY_FAIL_ON_MISSING — new constant.
  • system.properties — explicit default with a one-line comment.
  • VelocimacroFactoryTest (new) — three unit tests covering fail-on-missing=true, fail-on-missing=false, and the default.

Why this is enough

engine.init() throws → VelocityUtil.getEngine() (VelocityUtil.java:65) rethrows as DotRuntimeException → propagates out of InitServlet.init() at line 199 → dotcms.started.up is never set → existing ServletContainerHealthCheck already gates /readyz on that flag → K8s readiness probe fails → no traffic routed → eventual pod restart gives EFS time to settle.

Companion ticket #35602 (VelocityHealthCheck) is still valuable as defense-in-depth — it catches the opt-out case (fail-on-missing=false) and any future post-init macro corruption that this flag wouldn't see.

Test plan

  • VelocimacroFactoryTest — 3/3 pass (fail-on-missing=true throws, false succeeds, default throws). Runtime ERROR/INFO log lines verified in test output.
  • VelocityUtilTest — 20/20 pass (no regression).
  • Local smoke test on real dotCMS startup: temporarily added bogus-smoke-test.vm to velocimacro.library, ran just dev-run. Observed:
    • ERROR runtime.VelocimacroFactory - Velocimacro : VM library not found : bogus-smoke-test.vm : ...
    • INFO runtime.VelocimacroFactory - Velocimacro libraries loaded: [VM_global_library.vm, dotCMS_library.vm, dotCMS_library_ext.vm] ; failed: [bogus-smoke-test.vm]
    • VelocityException: Velocimacro : required VM libraries failed to load: [bogus-smoke-test.vm]. Set velocimacro.library.fail-on-missing=false to allow startup with missing libraries.
    • Stack trace propagation through InitServlet.init(InitServlet.java:199) and rethrow as DotRuntimeException
    • InitServlet init Completed absent from log (init aborted before dotcms.started.up=true)
    • /dotmgt/readyz returns HTTP 503
    • /dotmgt/livez returns HTTP 503

🤖 Generated with Claude Code

This PR fixes: #35601

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 20, 2026

Claude finished @dsolistorres's task in 2m 39s —— View job


dotCMS Backend Review

  • Load dotCMS conventions
  • Get the Java diff
  • Spawn specialized sub-agents (Security, Database, Java Standards, REST API)
  • Consolidate findings — all four returned NO_FINDINGS
  • Post consolidated review comment
  • Submit formal GitHub review — blocked: the action runner cannot submit formal PR reviews; the consolidated comment above is the canonical output

dotCMS Backend Review: no issues found.
· Branch

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 20, 2026

dotCMS Backend Review: no issues found.

@dsilvam dsilvam added this pull request to the merge queue May 20, 2026
@wezell wezell removed this pull request from the merge queue due to a manual request May 20, 2026
Comment thread dotCMS/src/main/resources/system.properties Outdated
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- Switch the fail-on-missing flag from a Velocity runtime property
  (rsvc.getBoolean / system.properties) to a dotCMS feature flag read
  via Config.getBooleanProperty("VELOCITY_LIBRARY_FAIL_ON_MISSING").
- Default to false to preserve historical behavior. Existing customers
  with optional/missing macro libraries continue to start normally;
  operators opt in once they have verified their library files load
  cleanly.

Removes the now-unused RuntimeConstants.VM_LIBRARY_FAIL_ON_MISSING and
the corresponding system.properties entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- Switch the fail-on-missing flag from a Velocity runtime property
  (rsvc.getBoolean / system.properties) to a dotCMS feature flag read
  via Config.getBooleanProperty("VELOCITY_LIBRARY_FAIL_ON_MISSING").
- Default to false to preserve historical behavior. Existing customers
  with optional/missing macro libraries continue to start normally;
  operators opt in once they have verified their library files load
  cleanly.

Removes the now-unused RuntimeConstants.VM_LIBRARY_FAIL_ON_MISSING and
the corresponding system.properties entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the issue-35601-velocity-fail-loud branch from 0e8d688 to 941c881 Compare May 21, 2026 16:32
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- VelocimacroFactory: pass `this` to Logger.error in the
  ResourceNotFoundException catch, matching the adjacent catch block and
  the repo-wide convention.
- VelocimacroFactoryTest: assert `verify(rsvc).getTemplate(MISSING_LIBRARY)`
  on the no-throw paths so a future refactor that short-circuits the load
  loop can't silently pass these tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- Switch the fail-on-missing flag from a Velocity runtime property
  (rsvc.getBoolean / system.properties) to a dotCMS feature flag read
  via Config.getBooleanProperty("VELOCITY_LIBRARY_FAIL_ON_MISSING").
- Default to false to preserve historical behavior. Existing customers
  with optional/missing macro libraries continue to start normally;
  operators opt in once they have verified their library files load
  cleanly.

Removes the now-unused RuntimeConstants.VM_LIBRARY_FAIL_ON_MISSING and
the corresponding system.properties entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- VelocimacroFactory: pass `this` to Logger.error in the
  ResourceNotFoundException catch, matching the adjacent catch block and
  the repo-wide convention.
- VelocimacroFactoryTest: assert `verify(rsvc).getTemplate(MISSING_LIBRARY)`
  on the no-throw paths so a future refactor that short-circuits the load
  loop can't silently pass these tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the issue-35601-velocity-fail-loud branch from 6d64f8e to f93ef0e Compare May 21, 2026 17:26
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- Switch the fail-on-missing flag from a Velocity runtime property
  (rsvc.getBoolean / system.properties) to a dotCMS feature flag read
  via Config.getBooleanProperty("VELOCITY_LIBRARY_FAIL_ON_MISSING").
- Default to false to preserve historical behavior. Existing customers
  with optional/missing macro libraries continue to start normally;
  operators opt in once they have verified their library files load
  cleanly.

Removes the now-unused RuntimeConstants.VM_LIBRARY_FAIL_ON_MISSING and
the corresponding system.properties entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the issue-35601-velocity-fail-loud branch from f93ef0e to a6f4529 Compare May 21, 2026 17:27
dsolistorres added a commit that referenced this pull request May 21, 2026
Addresses PR #35768 review feedback:

- VelocimacroFactory: pass `this` to Logger.error in the
  ResourceNotFoundException catch, matching the adjacent catch block and
  the repo-wide convention.
- VelocimacroFactoryTest: assert `verify(rsvc).getTemplate(MISSING_LIBRARY)`
  on the no-throw paths so a future refactor that short-circuits the load
  loop can't silently pass these tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the issue-35601-velocity-fail-loud branch from f93ef0e to a6f4529 Compare May 21, 2026 17:28
Copy link
Copy Markdown
Member

@wezell wezell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better, thanks Daniel!

dsolistorres added a commit that referenced this pull request May 21, 2026
…test (#35601)

Addresses PR #35768 review feedback:

- Add a comment at the throw site noting that this exception is terminal:
  the factory ends in a partial state (permission setup, namespace usage,
  and autoreload never run) and MUST be discarded. Documents the
  current contract so a future caller does not catch VelocityException
  and try to reuse the instance.
- Add an assertion in initThrowsWhenFlagIsTrue that the exception message
  contains FLAG_KEY, pinning the actionable opt-out hint so a future
  refactor cannot silently drop it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dsolistorres and others added 5 commits May 21, 2026 14:04
VelocimacroFactory silently swallowed ResourceNotFoundException when loading
velocimacro.library files at engine init, allowing init to "succeed" with no
global macros registered. After K8s pod restarts with transient I/O errors
(notably on EFS-backed volumes), #renderMarks and #editContentlet rendered as
literal text on every page until the JVM was restarted.

The catch now logs the failed library name at ERROR and tracks failures so
init can throw VelocityException when any required library failed. A new
flag velocimacro.library.fail-on-missing (default true) lets self-hosted
operators preserve the legacy silent-warn behavior if they intentionally
configure optional libraries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses PR #35768 review feedback:

- Switch the fail-on-missing flag from a Velocity runtime property
  (rsvc.getBoolean / system.properties) to a dotCMS feature flag read
  via Config.getBooleanProperty("VELOCITY_LIBRARY_FAIL_ON_MISSING").
- Default to false to preserve historical behavior. Existing customers
  with optional/missing macro libraries continue to start normally;
  operators opt in once they have verified their library files load
  cleanly.

Removes the now-unused RuntimeConstants.VM_LIBRARY_FAIL_ON_MISSING and
the corresponding system.properties entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses PR #35768 review feedback:

- VelocimacroFactory: pass `this` to Logger.error in the
  ResourceNotFoundException catch, matching the adjacent catch block and
  the repo-wide convention.
- VelocimacroFactoryTest: assert `verify(rsvc).getTemplate(MISSING_LIBRARY)`
  on the no-throw paths so a future refactor that short-circuits the load
  loop can't silently pass these tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…test (#35601)

Addresses PR #35768 review feedback:

- Add a comment at the throw site noting that this exception is terminal:
  the factory ends in a partial state (permission setup, namespace usage,
  and autoreload never run) and MUST be discarded. Documents the
  current contract so a future caller does not catch VelocityException
  and try to reuse the instance.
- Add an assertion in initThrowsWhenFlagIsTrue that the exception message
  contains FLAG_KEY, pinning the actionable opt-out hint so a future
  refactor cannot silently drop it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the issue-35601-velocity-fail-loud branch from f18be4b to a0f36b7 Compare May 21, 2026 20:04
@dsolistorres dsolistorres enabled auto-merge May 21, 2026 20:04
@dsolistorres dsolistorres added this pull request to the merge queue May 21, 2026
Merged via the queue into main with commit e1f9d74 May 22, 2026
72 of 86 checks passed
@dsolistorres dsolistorres deleted the issue-35601-velocity-fail-loud branch May 22, 2026 00:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Velocity: fail loud when global macro library load fails (follow-up to spike #35329)

3 participants