we-promise/sure · error · ArgumentError

Unable to parse transaction date

Error message

Unable to parse transaction date

What it means

AkahuEntry::Processor#date converts data[:date] to a Date by type: ISO-ish strings go through Time.parse then in_time_zone(family timezone); epoch Integer/Float through Time.at; Time/DateTime re-zoned; Date passed through. Two failure layers produce the same user-visible ArgumentError("Unable to parse transaction date"): (a) an unparseable String raises inside Time.parse; (b) an unknown type hits the internal raise "Invalid date format". Both are rescued (ArgumentError, TypeError) — TypeError covers nil-adjacent arithmetic like Time.at(nil) — logged, and re-raised with the stable message. Timezone lookup failures (family.timezone nil/bogus) also surface here since in_time_zone is inside the rescue.

Source

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

      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
        value.in_time_zone(account&.family&.timezone).to_date
      when Date
        value
      else
        Rails.logger.error("Akahu transaction has invalid date value")
        raise ArgumentError, "Invalid date format"
      end
    rescue ArgumentError, TypeError => e
      Rails.logger.error("Failed to parse Akahu transaction date: #{e.class}")
      raise ArgumentError, "Unable to parse transaction date"
    end

    def extra_metadata
      {
        "akahu" => {
          "pending" => pending?,
          "type" => data[:type],
          "category" => category_data[:name],
          "category_id" => category_data[:_id].presence || category_data[:id],
          "category_group" => category_group_name,
          "reference" => meta_data[:reference],
          "particulars" => meta_data[:particulars],
          "code" => meta_data[:code],
          "other_account" => meta_data[:other_account]
        }.compact
      }
    end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Look at the logged line "Failed to parse Akahu transaction date" plus the raw transaction (log data[:date].inspect) to identify whether it's the value or the type
  2. Ensure the account's family has a valid timezone (account&.family&.timezone) — a nil timezone poisons every date conversion for that family
  3. If the feed genuinely changed format, extend the String branch to try Date.parse or explicit formats before raising
  4. Quarantine the malformed transaction and let the sync continue — one bad date shouldn't block the whole import

Example fix

# before
def date
  value = data[:date]
  case value
  when String
    Time.parse(value).in_time_zone(account&.family&.timezone).to_date # "garbage" -> ArgumentError
  ...

# after
def date
  value = data[:date]
  case value
  when String
    tz = account&.family&.timezone || "UTC"
    (Time.parse(value) rescue (return nil)).in_time_zone(tz).to_date
  when Integer, Float
    Time.at(value).in_time_zone(account&.family&.timezone || "UTC").to_date
  ...
end
# caller: skip/save-without-date when nil is returned
Defensive patterns

Strategy: validation

Validate before calling

# Before processing a transaction
def valid_date_payload?(value)
  case value
  when Date, Time, DateTime then true
  when Integer, Float then true
  when String
    begin; !!Time.parse(value); rescue ArgumentError; false; end
  else false
  end
end
abort_row unless valid_date_payload?(data[:date]) && account&.family&.timezone.present?

Type guard

def akahu_date_convertible?(value)
  case value
  when Date, Time, DateTime, Integer, Float then true
  when String then (Time.parse(value) rescue false) ? true : false
  else false
  end
end

Try / catch

rescue ArgumentError => e
  if e.message == "Unable to parse transaction date"
    # skip/quarantine the row; log value + family timezone; continue sync
    next
  else
    raise
  end
end

Prevention

When it happens

Trigger: Akahu sends date as "31/02/2024" (impossible date) or "FY24-Q1" (garbage); a schema change delivers date as a Hash; the account's family has no timezone set making in_time_zone raise; epoch passed as a String containing digits (String branch -> Time.parse("1690000000") succeeds but a String "abc123" fails); VCR cassette hand-edited with a malformed date field.

Common situations: Bank/feed data glitches producing one corrupt row; API version changes flipping date from ISO 8601 to another format; sandbox accounts with placeholder data; multi-timezone setups where family.timezone is nil because the family record predates the timezone column.

Understand the failure class

Related errors


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