{"record":{"id":"62b6036ccce33402","repo":"puppetlabs/puppet","slug":"a-timestamp-cannot-be-multiplied-by-klass","errorCode":null,"errorMessage":"A Timestamp cannot be multiplied by %{klass}","messagePattern":"A Timestamp cannot be multiplied by %(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/time/timespan.rb","lineNumber":163,"sourceCode":"        Timespan.new(@nsecs - o.nsecs)\n      when Integer, Float\n        # Subtract seconds\n        Timespan.new(@nsecs - (o * NSECS_PER_SEC).to_i)\n      else\n        raise ArgumentError, _(\"%{klass} cannot be subtracted from a Timespan\") % { klass: a_an_uc(o) }\n      end\n    end\n\n    def -@\n      Timespan.new(-@nsecs)\n    end\n\n    def *(o)\n      case o\n      when Integer, Float\n        Timespan.new((@nsecs * o).to_i)\n      else\n        raise ArgumentError, _(\"A Timestamp cannot be multiplied by %{klass}\") % { klass: a_an(o) }\n      end\n    end\n\n    def divmod(o)\n      case o\n      when Integer\n        to_i.divmod(o)\n      when Float\n        to_f.divmod(o)\n      else\n        raise ArgumentError, _(\"Can not do modulus on a Timespan using a %{klass}\") % { klass: a_an(o) }\n      end\n    end\n\n    def modulo(o)\n      divmod(o)[1]\n    end\n","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/time/timespan.rb#L145-L181","documentation":"Timespan#* only multiplies by Integer or Float and raises ArgumentError otherwise. Note the message text is misleading in Puppet itself: it says 'A Timestamp cannot be multiplied by ...' even though this is the Timespan#* operator - the string was copied from timestamp.rb. Trust the operand semantics, not the message wording.","triggerScenarios":"ts * '2', ts * nil, ts * another_timespan, or ts * a Decimal/BigDecimal type from Puppet's type system. Only Integer and Float pass the case statement.","commonSituations":"Scaling a duration by a factor pulled from Hiera that arrived as a String; developers confused by the error message into thinking their Timestamp is at fault when the receiver is actually a Timespan; multiplying two durations together by mistake.","solutions":["Recognize the message wording is a known copy-paste artifact - your receiver is a Timespan and only the right operand is at fault","Convert the factor: ts * factor.to_i (or .to_f for fractional scaling)","If multiplying two durations was intentional, redesign - scale by a dimensionless number instead","Guard with factor.is_a?(Integer) || factor.is_a?(Float) before the expression"],"exampleFix":"// before\nscaled = span * lookup('scale_factor')   # String \"2\"\n\n// after\nf = lookup('scale_factor')\nscaled = span * (f.is_a?(String) ? f.to_f : f)","handlingStrategy":"type-guard","validationCode":"raise ArgumentError, \"factor must be Numeric, got #{o.class}\" unless o.is_a?(Integer) || o.is_a?(Float)\nscaled = ts * o","typeGuard":"def numeric_factor?(o)\n  o.is_a?(Integer) || o.is_a?(Float)\nend","tryCatchPattern":"begin\n  ts * factor\nrescue ArgumentError => e\n  # note: message says 'Timestamp' but receiver is a Timespan (known wording quirk)\n  factor = factor.to_f rescue raise DataError, e.message\n  retry\nend","preventionTips":["Ignore the misleading 'Timestamp' wording in the message - always inspect the right operand","Coerce scale factors from config with .to_f at load time","Never multiply two durations; scale by a dimensionless number"],"tags":["puppet","timespan","arithmetic","argument-error","misleading-message"],"backgroundTag":"invalid-operand-type","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}