{"record":{"id":"fcad3039b8f45b98","repo":"puppetlabs/puppet","slug":"klass-cannot-be-subtracted-from-a-timestamp","errorCode":null,"errorMessage":"%{klass} cannot be subtracted from a Timestamp","messagePattern":"%(.+?) cannot be subtracted from a Timestamp","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/time/timestamp.rb","lineNumber":150,"sourceCode":"    when Integer, Float\n      Timestamp.new(@nsecs + (o * NSECS_PER_SEC).to_i)\n    else\n      raise ArgumentError, _(\"%{klass} cannot be added to a Timestamp\") % { klass: a_an_uc(o) }\n    end\n  end\n\n  def -(o)\n    case o\n    when Timestamp\n      # Diff between two timestamps is a timespan\n      Timespan.new(@nsecs - o.nsecs)\n    when Timespan\n      Timestamp.new(@nsecs - o.nsecs)\n    when Integer, Float\n      # Subtract seconds\n      Timestamp.new(@nsecs - (o * NSECS_PER_SEC).to_i)\n    else\n      raise ArgumentError, _(\"%{klass} cannot be subtracted from a Timestamp\") % { klass: a_an_uc(o) }\n    end\n  end\n\n  def format(format, timezone = nil)\n    self.class.format_time(format, to_time, timezone)\n  end\n\n  def to_s\n    format(DEFAULT_FORMATS[0])\n  end\n\n  def to_time\n    ::Time.at(to_r).utc\n  end\nend\nend\nend\n","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/time/timestamp.rb#L132-L168","documentation":"Timestamp#- accepts a Timestamp (result: Timespan difference), a Timespan (result: Timestamp), or Integer/Float seconds (result: Timestamp). Everything else raises '%{klass} cannot be subtracted from a Timestamp'. This mirrors Timespan#- but, unlike it, does allow Timestamp operands because two points define a duration.","triggerScenarios":"ts - '10', ts - nil, ts - [60], ts - some_array or hash, or a Puppet Decimal/Sensitive wrapper on the right side.","commonSituations":"Subtracting a TTL or margin loaded from config that arrived as String; nil when an optional 'since' parameter was not set; passing a duration object from another library (ActiveSupport::Duration) that is not a Puppet Timespan.","solutions":["Convert numeric strings first: ts - margin.to_i","Wrap foreign duration objects into a Puppet Timespan before subtracting","Default optional parameters: (params['margin'] || 0)","Add an explicit case guard for Timestamp/Timespan/Integer/Float with a domain-specific error"],"exampleFix":"// before\nremaining = deadline - lookup('margin')   # String \"300\"\n\n// after\nremaining = deadline - Integer(lookup('margin'))","handlingStrategy":"type-guard","validationCode":"ok = o.is_a?(Puppet::Pops::Time::Timestamp) || o.is_a?(Puppet::Pops::Time::Timespan) || o.is_a?(Integer) || o.is_a?(Float)\nraise ArgumentError, \"cannot subtract #{o.class} from Timestamp\" unless ok\nts - o","typeGuard":"def timestamp_subtractable?(o)\n  o.is_a?(Puppet::Pops::Time::Timestamp) || o.is_a?(Puppet::Pops::Time::Timespan) ||\n    o.is_a?(Integer) || o.is_a?(Float)\nend","tryCatchPattern":"begin\n  ts - o\nrescue ArgumentError => e\n  raise DataError, \"bad subtrahend #{o.inspect} for Timestamp\"\nend","preventionTips":["Wrap foreign duration types (e.g. ActiveSupport::Duration) into Puppet Timespan first","Default optional margins: (params['margin'] || 0)","Validate catalog data with assert_type before it reaches Ruby arithmetic"],"tags":["puppet","timestamp","arithmetic","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"}