Shopify/liquid · error · Liquid::FileSystemError

Illegal template path '#

Error message

Illegal template path '#{File.expand_path(full_path)}'

What it means

After expanding the template name against root, full_path re-checks that File.expand_path(full_path) still starts with File.expand_path(root); otherwise it raises Liquid::FileSystemError with the expanded path. This is the final defense against names that resolve outside the template root (e.g. via symlinks or '..' slipping past the regex).

Solutions

  1. Fix the template name so it resolves inside the root (remove '..' segments).
  2. Set root to an absolute, canonical directory (File.expand_path) when constructing LocalFileSystem.
  3. Remove or replace symlinks in the template directory that point outside the root.
  4. Log/inspect the expanded path in the message to see where the resolution went wrong.

Example fix

// before
Liquid::LocalFileSystem.new('templates/../views', '%s.liquid')
// after
Liquid::LocalFileSystem.new(File.expand_path('views'), '%s.liquid')
Defensive patterns

Strategy: validation

Validate before calling

def within_root?(fs, name)
  File.expand_path(fs.full_path(name)).start_with?(File.expand_path(fs.root))
rescue Liquid::FileSystemError
  false
end

Type guard

def safe_resolve(fs, name)
  full = File.expand_path(fs.full_path(name))
  full.start_with?(File.expand_path(fs.root)) ? full : nil
rescue Liquid::FileSystemError
  nil
end

Try / catch

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

Prevention

When it happens

Trigger: A template name whose resolved absolute path escapes the configured root — typically via '..' segments, a misconfigured root, or a symlink inside the template directory pointing elsewhere.

Common situations: Configuring root with a relative or trailing-slash path that expands differently than expected; template directories containing symlinks to other locations; names combining subdirectories with '..'.

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/6b82093bdda62852. Report an issue: GitHub.

Appendix: source

Thrown at lib/liquid/file_system.rb:70

    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)