{"record":{"id":"b319a90dd7a70b19","repo":"Shopify/liquid","slug":"undefined-variable-key-b319a9","errorCode":null,"errorMessage":"undefined variable #{key}","messagePattern":"undefined variable #(.+?)","errorType":"exception","errorClass":"Liquid::UndefinedVariable","httpStatus":null,"severity":"warning","filePath":"lib/liquid/variable_lookup.rb","lineNumber":83,"sourceCode":"          object = res.to_liquid\n\n          # Some special cases. If the part wasn't in square brackets and\n          # no key with the same name was found we interpret following calls\n          # as commands and call them on the current object\n        elsif lookup_command?(i) && object.respond_to?(key)\n          object = object.send(key).to_liquid\n\n        # Handle string first/last like ActiveSupport does (returns first/last character)\n        # ActiveSupport returns \"\" for empty strings, not nil\n        elsif lookup_command?(i) && object.is_a?(String) && (key == \"first\" || key == \"last\")\n          object = key == \"first\" ? (object[0] || \"\") : (object[-1] || \"\")\n\n          # No key was present with the desired value and it wasn't one of the directly supported\n          # keywords either. The only thing we got left is to return nil or\n          # raise an exception if `strict_variables` option is set to true\n        else\n          return nil unless context.strict_variables\n          raise Liquid::UndefinedVariable, \"undefined variable #{key}\"\n        end\n\n        # If we are dealing with a drop here we have to\n        object.context = context if object.respond_to?(:context=)\n      end\n\n      object\n    end\n\n    def ==(other)\n      self.class == other.class && state == other.state\n    end\n\n    protected\n\n    def state\n      [@name, @lookups, @command_flags]\n    end","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/Shopify/liquid/blob/807d45a6b3d4568e64e86b375e3702df2c7c860c/lib/liquid/variable_lookup.rb#L65-L101","documentation":"VariableLookup#evaluate raises Liquid::UndefinedVariable, \"undefined variable #{key}\", only when the context's strict_variables option is enabled and a lookup key resolves to nothing (and is not a supported keyword). Without strict_variables the same situation silently returns nil.","triggerScenarios":"Rendering with context strict_variables: true (often paired with strict_filters) while a template references {{ missing_key }} or {{ obj.missing_attr }} that no assign/register provides.","commonSituations":"Teams enabling strict_variables in CI to catch template typos; renamed variables in Ruby code not updated in templates; partials rendered with different assigns than expected.","solutions":["Provide the variable in the assigns Hash or registers before rendering.","Fix the template to reference an existing key (or use {{ key | default: '' }}).","Guard with {% if key %} or use the default filter so the lookup is only evaluated when present.","If nil is a legitimate value, disable strict_variables for that render."],"exampleFix":"// before\ntemplate.render({}, strict_variables: true) # template: {{ user_name }}\n// after\ntemplate.render({ 'user_name' => 'bob' }, strict_variables: true)","handlingStrategy":"try-catch","validationCode":"required_keys.each { |k| raise \"missing template variable #{k}\" unless assigns.key?(k) }","typeGuard":"def defined_in_context?(key, assigns)\n  assigns.key?(key) || assigns.key?(key.to_s) || assigns.key?(key.to_sym)\nend","tryCatchPattern":"begin\n  template.render(assigns, strict_variables: true)\nrescue Liquid::UndefinedVariable => e\n  logger.warn(\"Template referenced #{e.message.sub('undefined variable ', '')}\")\n  template.render(assigns)\nend","preventionTips":["Keep a contract list of required template variables and assert before render","Use {% if var %} or | default: filters in templates","Enable strict_variables only in CI/test runs to surface missing keys early"],"tags":["liquid","undefined-variable","strict-variables","template"],"backgroundTag":"undefined-variable","analyzedSha":"807d45a6b3d4568e64e86b375e3702df2c7c860c","analyzedAt":"2026-09-08T11:31:38.917Z","contentChangedAt":"2026-09-08T11:31:38.917Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}