Shopify/liquid · error · Liquid::MethodOverrideError
Filter overrides registered public methods as non public: #
Error message
Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')} What it means
`StrainerTemplate.add_filter` raises Liquid::MethodOverrideError when a filter module defines a method that collides with an already-registered invokable method but as private/protected instead of public. This protects Liquid's internal invariants: any method invokable from templates must remain public, otherwise filter invocation would break silently.
Solutions
- Rename the private/protected method so it does not collide with registered public methods.
- Move the `private` keyword below the invokable filter methods, or make the colliding method public.
- Audit custom filter modules with `MyFilter.private_instance_methods & Liquid::StrainerTemplate.methods` before registration.
- Split helper logic into a separate non-filter module included only for internal use.
- Update the filter registration order or remove duplicate conflicting filter modules.
Example fix
// before
module MyFilter
private
def truncate(input, n)
input[0, n]
end
end
// after
module MyFilter
def truncate(input, n)
input[0, n]
end
private :truncate # only if it is NOT meant to be invokable — otherwise keep public
end Defensive patterns
Strategy: validation
Validate before calling
# before register_filter
conflicts = (MyFilter.private_instance_methods + MyFilter.protected_instance_methods) & Liquid::StrainerTemplate.instance_methods
raise "visibility conflict: #{conflicts}" unless conflicts.empty? Type guard
def safe_to_register?(filter)
(filter.private_instance_methods + filter.protected_instance_methods)
.none? { |m| Liquid::StrainerTemplate.method_defined?(m) }
end Try / catch
begin
Liquid::Template.register_filter(MyFilter)
rescue Liquid::MethodOverrideError => e
logger.error("filter registration failed: #{e.message}")
end Prevention
- Keep all template-invokable filter methods public
- Place `private` keywords carefully in filter modules
- Avoid method names colliding with built-in filters (size, capitalize, etc.)
- Run a registration smoke test at boot in CI
When it happens
Trigger: Registering a filter module with `Liquid::Template.register_filter(MyFilter)` where MyFilter defines a private/protected instance method whose name matches an existing public invokable method (e.g. redefining `size`, `capitalize` or a core filter as private).
Common situations: Custom filter modules inheriting from a base class that marks methods private; refactoring a public filter to a private helper without renaming; loading two filter modules where the second narrows visibility of a shared method name; ruby `private` keyword accidentally placed above filter definitions.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- This liquid context does not allow includes.
- Profiler not loaded, require 'liquid/profiler' first
- errors.syntax.unexpected_else (locale: Unexpected 'else'…
- errors.syntax.invalid_delimiter
- errors.syntax.unknown_tag (locale: 'Unknown tag 'tag'')
AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08).
Data as JSON: /api/errors/e249a03eb8b2b785.
Report an issue: GitHub.
Appendix: source
Thrown at lib/liquid/strainer_template.rb:22
module Liquid
# StrainerTemplate is the computed class for the filters system.
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
#
# The Strainer only allows method calls defined in filters given to it via StrainerFactory.add_global_filter,
# Context#add_filters or Template.register_filter
class StrainerTemplate
def initialize(context)
@context = context
end
class << self
def add_filter(filter)
return if include?(filter)
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
if invokable_non_public_methods.any?
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
end
include(filter)
filter_methods.merge(filter.public_instance_methods.map(&:to_s))
end
def invokable?(method)
filter_methods.include?(method.to_s)
end
def inherited(subclass)
super
subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
end
def filter_method_names
filter_methods.map(&:to_s).to_aView on GitHub (pinned to 807d45a6b3)