{"record":{"id":"7a7a45e7658e35b7","repo":"Shopify/liquid","slug":"e-message-re-raised-as-liquid-argumenterror-from","errorCode":null,"errorMessage":"e.message (re-raised as Liquid::ArgumentError from ::ArgumentError during operator invocation)","messagePattern":"e\\.message \\(re-raised as Liquid::ArgumentError from ::ArgumentError during operator invocation\\)","errorType":"exception","errorClass":"Liquid::ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/liquid/condition.rb","lineNumber":194,"sourceCode":"\n    def interpret_condition(left, right, op, context)\n      # If the operator is empty this means that the decision statement is just\n      # a single variable. We can just poll this variable from the context and\n      # return this as the result.\n      return context.evaluate(left) if op.nil?\n\n      left  = Liquid::Utils.to_liquid_value(context.evaluate(left))\n      right = Liquid::Utils.to_liquid_value(context.evaluate(right))\n\n      operation = self.class.operators[op] || raise(Liquid::ArgumentError, \"Unknown operator #{op}\")\n\n      if operation.respond_to?(:call)\n        operation.call(self, left, right)\n      elsif left.respond_to?(operation) && right.respond_to?(operation) && !left.is_a?(Hash) && !right.is_a?(Hash)\n        begin\n          left.send(operation, right)\n        rescue ::ArgumentError => e\n          raise Liquid::ArgumentError, e.message\n        end\n      end\n    end\n\n    def deprecated_default_context\n      warn(\"DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated \" \\\n        \"and will be removed from Liquid 6.0.0.\")\n      Context.new\n    end\n\n    class ParseTreeVisitor < Liquid::ParseTreeVisitor\n      def children\n        [\n          @node.left,\n          @node.right,\n          @node.child_condition,\n          @node.attachment\n        ].compact","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/Shopify/liquid/blob/807d45a6b3d4568e64e86b375e3702df2c7c860c/lib/liquid/condition.rb#L176-L212","documentation":"When a comparison operator is applied via `left.send(operation, right)` and Ruby raises ::ArgumentError, Liquid re-raises it as Liquid::ArgumentError carrying the original message. This happens when an object's operator method (e.g. <=>, ==, a custom method) rejects the right operand's type, or the object misuses its own API internally.","triggerScenarios":"Comparing incompatible types in `{% if x > y %}` (e.g. String vs Integer with a String#> that raises); custom drop/objects defining operators that raise ArgumentError; using an operator name that resolves to a method with wrong arity; non-Hash operands where left/right both respond_to? the operator.","commonSituations":"Comparing nil-wrapped values or strings that look numeric; custom Liquid drops with badly implemented <=> or respond_to?; template conditions on locale-formatted numbers; upgrading Ruby changing operator semantics.","solutions":["Fix the comparison in the template so both sides share a type (e.g. compare numbers to numbers).","Coerce values before comparison via a filter or by assigning normalized variables.","If you own a custom drop/object, correct its operator implementation or arity.","Wrap render in error handling to log e.message and identify the offending method."],"exampleFix":"// before\n{% if price > \"100\" %}...{% endif %}\n// after\n{% assign limit = 100 %}\n{% if price > limit %}...{% endif %}","handlingStrategy":"type-guard","validationCode":"def comparable?(a, b)\n  (a.is_a?(Numeric) && b.is_a?(Numeric)) || (a.is_a?(String) && b.is_a?(String))\nend\n# guard template conditions: only compare same-typed values","typeGuard":"def safe_compare(left, right, op)\n  return false unless left.respond_to?(op) && right.respond_to?(op)\n  return false unless left.class == right.class || (left.is_a?(Numeric) && right.is_a?(Numeric))\n  true\nend","tryCatchPattern":"begin\n  html = template.render(assigns)\nrescue Liquid::ArgumentError => e\n  logger.warn(\"liquid comparison failed: #{e.message}\")\n  html = template.render(normalized_assigns)\nend","preventionTips":["Normalize variable types (numbers vs strings) before comparing in templates.","Implement <=>/== correctly in custom drops.","Avoid comparing nil or formatted (locale) strings as numbers.","Test templates with representative render data."],"tags":["liquid","comparison","argument-error","type-mismatch"],"backgroundTag":"type-mismatch","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"}