Skip to content

Report the errors swallowed while computing a cache digest - #2712

Open
erikaxel wants to merge 1 commit into
ViewComponent:mainfrom
erikaxel:vc-digest-report-swallowed-errors
Open

Report the errors swallowed while computing a cache digest#2712
erikaxel wants to merge 1 commit into
ViewComponent:mainfrom
erikaxel:vc-digest-report-swallowed-errors

Conversation

@erikaxel

@erikaxel erikaxel commented Sep 3, 2026

Copy link
Copy Markdown

Problem

Every rescue in the digest machinery returns a neutral value and says nothing:

  • CacheDigest::Resolver#find_templates[]
  • CacheDigest.constantize_componentnil
  • CacheDigest.partial_paths_in[]
  • DependencyTracking#find_dependencies → falls back to super

The rationale in the source is right — a broken digest is preferable to a broken render, and that shouldn't change for production. But it means a misconfiguration, a raising inherited hook, or a failed autoload is completely invisible: the digest quietly degrades to "no component dependencies", <% cache %> blocks stop being invalidated, and the application serves stale HTML.

That failure mode is indistinguishable from a component that was simply never cached, which is the problem while adopting the feature. Evaluating it, we spent a while unable to tell whether we were looking at a bug, a misconfiguration, or intended behaviour, because nothing anywhere reported a problem.

Fix

All four sites now go through CacheDigest.handle_error, which either logs the exception at warn through ActiveSupport's logger:

[ViewComponent] Ignored an error while resolving PostComponent: NoMethodError: ...

or re-raises it, controlled by a new option:

config.view_component.raise_on_cache_digest_errors

It defaults to Rails.env.local?: raise in development and test, where a silently untracked component is a bug you want to see, and swallow-and-log in production, where the existing guarantee holds. Applications that would rather keep the current behaviour everywhere set it to false.

Nothing about what is swallowed changed — only whether anyone finds out. The neutral return values, and the comments explaining why each one is the right degradation, are unchanged.

If you'd rather this shipped as logging only, flipping the default to false is a one-line change in ViewComponent::Config and I'm happy to make it.

Tests

The four swallow sites already had tests. Each now also asserts the logged message, and each gained a counterpart asserting the exception propagates when raise_on_cache_digest_errors is on:

  • test_partial_path_extraction_swallows_parser_errors / ..._raises_parser_errors_locally
  • test_resolver_returns_no_template_when_synthesis_fails / test_resolver_raises_when_synthesis_fails_locally
  • test_dependency_tracking_falls_back_when_scanning_fails / ..._raises_when_scanning_fails_locally
  • test_constantizing_swallows_unexpected_errors / test_constantizing_raises_unexpected_errors_locally

Plus test_digest_errors_are_swallowed_without_a_logger for the no-Rails-logger case, and the new default in ConfigTest.

bundle exec rake test, engine_test, spec and eager_load_check pass on Rails 8.1, and the caching and config suites pass on 7.1, 7.2 and 8.0. The only failures seen anywhere were pre-existing and unrelated: the allocation-count matrix in RenderingAllocationsTest has no entry for Rails 7.1 on Ruby 3.4.

Notes

Docs: a "When a digest can't be computed" section in the caching guide, and the config option in docs/api.md.

This is part of a small set of digest issues found while evaluating ExperimentallyCacheable; the other is #2709. They're independent and can be merged in either order.

@erikaxel
erikaxel force-pushed the vc-digest-report-swallowed-errors branch from 0c7ba27 to 3f3f1c3 Compare September 3, 2026 11:55
Every rescue in the digest machinery returns a neutral value, so a
misconfiguration, an autoload failure, or a raising `inherited` hook
degrades to "no component dependencies" and the application serves
stale HTML with nothing reported anywhere.

- Route the four swallow sites through CacheDigest.handle_error
- Log at `warn` through ActiveSupport's logger, preserving the production
  guarantee that a stale fragment beats a failed render
- Raise instead in local environments, configurable with
  config.view_component.raise_on_cache_digest_errors
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.

1 participant