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, DateTimeView on GitHub (pinned to e69894adb9)
Solutions
- 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
- Normalize the string before it reaches the processor: strip currency symbols and commas ("$1,234.56" -> "1234.56")
- 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
- 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
- Never assume feed numeric fields are raw; strip separators/symbols at the boundary
- Log the offending value, not just the error class, when wrapping parse failures
- Add fixtures with "1,234.56", "$10.00", "" to import tests
- Pin down API payloads in cassettes so upstream format changes surface as diff, not runtime crash
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.