{"record":{"id":"a9e4f866032c9008","repo":"puppetlabs/puppet","slug":"can-not-do-modulus-on-a-timespan-using-a-klass","errorCode":null,"errorMessage":"Can not do modulus on a Timespan using a %{klass}","messagePattern":"Can not do modulus on a Timespan using a %(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/time/timespan.rb","lineNumber":174,"sourceCode":"    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\n    def %(o)\n      modulo(o)\n    end\n\n    def div(o)\n      case o\n      when Timespan\n        # Timespan/Timespan yields a Float\n        @nsecs.fdiv(o.nsecs)\n      when Integer, Float\n        Timespan.new(@nsecs.div(o))","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/time/timespan.rb#L156-L192","documentation":"Timespan#divmod (and therefore #modulo and #%) only works when the divisor is an Integer (delegated to to_i.divmod) or a Float (delegated to to_f.divmod). Any other class raises ArgumentError with 'Can not do modulus on a Timespan using a %{klass}'.","triggerScenarios":"ts % '60', ts % nil, ts % another_timespan, or ts % a Rational. Also Puppet DSL like $span % $var where $var is a String from a fact or Hiera.","commonSituations":"Bucketing durations into intervals (span % 3600) with the bucket size loaded from config as a String; reusing Ruby's Numeric %-style idims with Puppet data; passing Puppet's Decimal type which is not a Ruby Float.","solutions":["Convert the modulus to a plain Ruby number first: ts % divisor.to_i","If you meant Timespan/Timespan division (ratio), use the div operator instead: span1 / span2 returns a Float","Validate config values with assert_type(Numeric, ...) in the Puppet DSL before they reach Ruby","Check for nil before the expression - optional config that was never set is the most common source"],"exampleFix":"// before\nbucket = span % lookup('bucket_seconds')   # \"900\" from Hiera\n\n// after\nbucket = span % Integer(lookup('bucket_seconds'))","handlingStrategy":"validation","validationCode":"raise ArgumentError, \"modulus must be Integer/Float\" unless divisor.is_a?(Integer) || divisor.is_a?(Float)\nbucket = ts % divisor","typeGuard":"def modulus_operand?(o)\n  o.is_a?(Integer) || o.is_a?(Float)\nend","tryCatchPattern":"begin\n  ts % m\nrescue ArgumentError => e\n  raise DataError, \"bad modulus #{m.inspect} for Timespan\"\nend","preventionTips":["Convert interval sizes from config once at startup: @bucket = Integer(config['bucket'])","For Timespan/Timespan ratios use div (/), not % - different operator, different rules","Assert Numeric in the Puppet DSL with assert_type before values reach Ruby"],"tags":["puppet","timespan","modulo","argument-error","type-mismatch"],"backgroundTag":"invalid-operand-type","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}