we-promise/sure · error · ArgumentError

Invalid transaction amount

Error message

Invalid transaction amount

What it means

AkahuEntry::Processor parses each Akahu transaction's amount into a BigDecimal (String via BigDecimal(str), Numeric via BigDecimal(to_s)) and then negates it (Akuhu's banking convention: negative = out) for Sure's storage convention. If the amount string is present but BigDecimal() cannot parse it, the ArgumentError is caught, the class is logged ("Failed to parse Akahu transaction amount: ArgumentError"), and it re-raises ArgumentError("Invalid transaction amount"). Note the else branch maps unknown types (nil, hash) to BigDecimal("0") — so this error specifically means: a String/Numeric that looks parseable but isn't.

Source

Thrown at app/models/akahu_entry/processor.rb:164

      nil
    end

    def amount
      parsed_amount = case data[:amount]
      when String
        BigDecimal(data[:amount])
      when Numeric
        BigDecimal(data[:amount].to_s)
      else
        BigDecimal("0")
      end

      # Akahu uses banking convention: negative is money out, positive is money in.
      # Sure stores expenses as positive and income as negative.
      -parsed_amount
    rescue ArgumentError => e
      Rails.logger.error "Failed to parse Akahu transaction amount: #{e.class}"
      raise ArgumentError, "Invalid transaction amount"
    end

    def currency
      parse_currency(data[:currency]) || akahu_account.currency || account&.currency || "NZD"
    end

    def date
      value = data[:date]
      case value
      when String
        if value.include?("T") || value.include?(":")
          Time.parse(value).in_time_zone(account&.family&.timezone).to_date
        else
          Date.parse(value)
        end
      when Integer, Float
        Time.at(value).in_time_zone(account&.family&.timezone).to_date
      when Time, DateTime

View on GitHub (pinned to e69894adb9)

Solutions

  1. Inspect the exact payload: the log line records the failure class; enable raw payload debug (compare with other providers' *_DEBUG_RAW pattern) or pry into data[:amount] to see the malformed value
  2. Normalize the string before it reaches the processor: strip currency symbols and commas ("$1,234.56" -> "1234.56")
  3. If Akahu genuinely changed the field format, patch parse_amount's String branch (e.g. data[:amount].to_s.gsub(/[^0-9.\-]/, "")) and add a regression test
  4. Skip/quarantine the offending transaction rather than aborting the whole sync if one row is bad — decide policy explicitly

Example fix

# before
def parse_amount
  parsed = case (a = data[:amount])
           when String then BigDecimal(a)      # "1,234.56" -> ArgumentError
           ...

# after
def parse_amount
  parsed = case (a = data[:amount])
           when String then BigDecimal(a.to_s.delete(",$").strip) # "1,234.56" -> 1234.56
           when Numeric then BigDecimal(a.to_s)
           else BigDecimal("0")
           end
  -parsed
rescue ArgumentError => e
  Rails.logger.error("Failed to parse Akahu transaction amount=#{data[:amount].inspect}")
  raise ArgumentError, "Invalid transaction amount"
end
Defensive patterns

Strategy: validation

Validate before calling

# Normalize before parse, in your layer feeding the processor
raw = data[:amount]
normalized =
  case raw
  when Numeric then raw.to_s
  when String then raw.to_s.gsub(/[^0-9.\-]/, "") # strips $ , spaces
  else "0"
  end
BigDecimal(normalized) rescue BigDecimal("0")

Type guard

def parseable_amount?(value)
  return true if value.is_a?(Numeric)
  begin
    BigDecimal(value.to_s.strip)
    true
  rescue ArgumentError
    false
  end
end

Try / catch

rescue ArgumentError => e
  if e.message == "Invalid transaction amount"
    # log data[:amount].inspect, quarantine this transaction, continue the sync
    Rails.logger.error("akahu amount=#{data[:amount].inspect}")
    next # or mark row skipped
  else
    raise
  end
end

Prevention

When it happens

Trigger: Akahu returns an amount as "1,234.56" (thousands separator), "12.34.56" (double dot), "" handled? no — empty string: BigDecimal("") raises ArgumentError, so a blank-but-present amount string trips this; localized decimal comma "12,50"; amounts with currency symbols "$45.00"; scientific notation edge strings some proxy normalizes; an API schema change surfacing amount as a formatted display string rather than raw decimal.

Common situations: Upstream Akahu API behavior changes (raw numeric field becomes display-formatted); an intermediate layer (custom proxy, VCR cassette editing, JSON transform) reformatting numbers; sandbox data with placeholder strings like "N/A"; regional formatting injected by a serialization library.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/022f834ac8ff039f. Report an issue: GitHub.