we-promise/sure · error · ArgumentError

Brex transaction missing required field 'id'

Error message

Brex transaction missing required field 'id'

What it means

Raised by BrexEntry::Processor#external_id (ArgumentError) when the Brex transaction payload's :id is nil or blank. The id is mandatory because it builds the deduplication key "brex_#{id}" passed to import_transaction. process() logs it and re-raises, so one malformed transaction aborts the sync batch unless the caller filters it.

Source

Thrown at app/models/brex_entry/processor.rb:62

  private
    attr_reader :brex_transaction, :brex_account

    def import_adapter
      @import_adapter ||= Account::ProviderImportAdapter.new(account)
    end

    def account
      @account ||= brex_account.current_account
    end

    def data
      @data ||= brex_transaction.with_indifferent_access
    end

    def external_id
      id = data[:id].presence
      raise ArgumentError, "Brex transaction missing required field 'id'" unless id

      "brex_#{id}"
    end

    def safe_external_id
      external_id
    rescue ArgumentError
      "brex_unknown"
    end

    def name
      data[:description].presence ||
        merchant_payload[:raw_descriptor].presence ||
        merchant_payload[:name].presence ||
        I18n.t("brex_items.entries.default_name")
    end

    def notes

View on GitHub (pinned to e69894adb9)

Solutions

  1. Inspect the failing payload (data) and confirm the id key — log data.keys for the failing transaction to catch shape drift immediately.
  2. Filter before processing: skip (and log) entries whose [:id] is blank so one bad record cannot kill the batch.
  3. If the shape changed upstream (renamed/nested field), update the extraction in BrexEntry::Processor or map it before enqueueing.
  4. Fix fixtures/tests to always include a realistic Brex transaction id.

Example fix

# before
transactions.each { |tx| BrexEntry::Processor.new(tx, brex_account: ba).process }

# after
transactions.each do |tx|
  unless tx.with_indifferent_access[:id].present?
    Rails.logger.warn("Skipping Brex transaction without id: #{tx.inspect}")
    next
  end
  BrexEntry::Processor.new(tx, brex_account: ba).process
end
Defensive patterns

Strategy: validation

Validate before calling

tx = transaction_payload.with_indifferent_access
return :skipped unless tx[:id].present? # every Brex transaction must carry an id

Type guard

# Ruby
def processable_brex_tx?(payload)
  payload.is_a?(Hash) && payload.with_indifferent_access[:id].present?
end

Try / catch

begin
  BrexEntry::Processor.new(tx, brex_account: ba).process
rescue ArgumentError => e
  raise unless e.message.include?("missing required field 'id'")
  Rails.logger.warn("Skipping Brex transaction without id: #{tx.inspect}")
  :skipped
end

Prevention

When it happens

Trigger: BinanceItem::Importer-style loop hands the processor a transaction hash lacking "id" — e.g. a Brex webhook body or test fixture shaped differently, an upstream API change renaming the field, an empty-string id, or a payload accidentally wrapped one level too deep (data[:id] nil because the hash is nested under "transaction").

Common situations: VCR fixtures recorded from a different API version; sandbox payloads with null ids; a payload-shape refactor upstream; JSON where the array element is { "transaction" => {...} } rather than the transaction itself.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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