Shopify/liquid · error · Liquid::FileSystemError

Illegal template name '#

Error message

Illegal template name '#{template_path}'

What it means

LocalFileSystem#full_path raises Liquid::FileSystemError when the template name fails the allowed pattern (must start with a non-dot, non-slash character and contain only alphanumerics, underscores, and slashes). This rejects names that could escape the template root or reference odd paths.

Solutions

  1. Sanitize the template name: strip extensions and disallowed characters before it reaches the include tag.
  2. Use only [a-zA-Z0-9_/] characters (no dots, hyphens, or leading slash) in template names.
  3. Never build include names from raw user input; map user choices to a whitelist of template names.
  4. If you need different naming, subclass LocalFileSystem and override full_path with your own validation.

Example fix

// before
{% include '{{ user_input }}' %}  # user_input = "blog/post.html"
// after
{% assign slug = user_input | split: '.' | first %}
{% include slug %}  # "blog/post" — safe characters only
Defensive patterns

Strategy: validation

Validate before calling

TEMPLATE_NAME_RE = /\A[a-zA-Z0-9_]+(?:\/[a-zA-Z0-9_]+)*\z/
def safe_template_name?(name)
  TEMPLATE_NAME_RE.match?(name.to_s)
end

Type guard

def sanitize_template_name(input)
  name = input.to_s.sub(/\.[^.]*\z/, '').gsub(/[^a-zA-Z0-9_\/]/, '')
  TEMPLATE_NAME_RE.match?(name) ? name : nil
end

Try / catch

begin
  tpl.render(ctx)
rescue Liquid::FileSystemError => e
  raise unless e.message.start_with?('Illegal template name')
  render_error_page('Invalid template reference')
end

Prevention

When it happens

Trigger: Passing a template name containing '.', '..', leading '/', spaces, hyphens, or other disallowed characters to {% include %}/{% render %}, e.g. {% include '../secret' %} or {% include 'my-file' %}.

Common situations: Dynamic include names built from user data or URLs (which contain dots/hyphens); attempting directory traversal via '../'; using file names with extensions inside the tag (the extension comes from the pattern instead).

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08). Data as JSON: /api/errors/bb9b1ea5e23f6116. Report an issue: GitHub.

Appendix: source

Thrown at lib/liquid/file_system.rb:62

  #   file_system.full_path("index") # => "/some/path/index.html"
  #
  class LocalFileSystem
    attr_accessor :root

    def initialize(root, pattern = "_%s.liquid")
      @root    = root
      @pattern = pattern
    end

    def read_template_file(template_path)
      full_path = full_path(template_path)
      raise FileSystemError, "No such template '#{template_path}'" unless File.exist?(full_path)

      File.read(full_path)
    end

    def full_path(template_path)
      raise FileSystemError, "Illegal template name '#{template_path}'" unless %r{\A[^./][a-zA-Z0-9_/]+\z}.match?(template_path)

      full_path = if template_path.include?('/')
        File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
      else
        File.join(root, @pattern % template_path)
      end

      raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path).start_with?(File.expand_path(root))

      full_path
    end
  end
end

View on GitHub (pinned to 807d45a6b3)