we-promise/sure · error · Error

bad_request

bad_request

Error message

Invalid account number format: #{account_number}

What it means

Raised by sanitize_account_number! before any HTTP call: account_number must be present and match /\A[A-Za-z0-9]+\z/. Indexa Capital account numbers are 8-char alphanumeric codes like "LPYH3MCQ", and this guard protects the URL path of /accounts/{account_number}/fiscal-results, /portfolio and /performance from injection and malformed paths.

Source

Thrown at app/models/provider/indexa_capital.rb:106

  def get_activities(account_number:, start_date: nil, end_date: nil)
    Rails.logger.info "Provider::IndexaCapital - No activities endpoint available for Indexa Capital API"
    []
  end

  private

    RETRYABLE_ERRORS = [
      SocketError, Net::OpenTimeout, Net::ReadTimeout,
      Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT, EOFError
    ].freeze

    MAX_RETRIES = 3
    INITIAL_RETRY_DELAY = 2 # seconds

    # Indexa Capital account numbers are 8-char alphanumeric (e.g., "LPYH3MCQ")
    def sanitize_account_number!(account_number)
      unless account_number.present? && account_number.match?(/\A[A-Za-z0-9]+\z/)
        raise Error.new("Invalid account number format: #{account_number}", :bad_request)
      end
    end

    attr_reader :username, :document, :password, :api_token

    def validate_configuration!
      return if @api_token.present?

      if @username.blank? || @document.blank? || @password.blank?
        raise ConfigurationError, "Either API token or all three username/document/password credentials are required"
      end
    end

    def token_auth?
      @api_token.present?
    end

    def with_retries(operation_name, max_retries: MAX_RETRIES)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Always source the value from list_accounts/extract_accounts output (the account_number key), never from user input or cross-provider IDs
  2. Strip whitespace when storing: account_number.to_s.strip
  3. Pre-validate with the same regex before entering a sync loop so bad records are skipped/logged, not fatal
  4. If a legitimate account number ever contains a dash, loosen the regex deliberately - but confirm with Indexa first

Example fix

# before
provider.get_holdings(account_number: account.external_id) # UUID like "0f8a..."

# after
provider.get_holdings(account_number: account.account_number.to_s.strip) # "LPYH3MCQ"
Defensive patterns

Strategy: validation

Validate before calling

INDEXA_ACCOUNT_NUMBER = /\A[A-Za-z0-9]{8}\z/ # provider requires alphanumeric
return unless (num = account_number.to_s.strip).match?(INDEXA_ACCOUNT_NUMBER)
provider.get_holdings(account_number: num)

Type guard

def valid_indexa_account_number?(value)
  value.to_s.strip.match?(/\A[A-Za-z0-9]{8}\z/)
end

Try / catch

begin
  provider.get_holdings(account_number: num)
rescue Provider::IndexaCapital::Error => e
  raise unless e.error_type == :bad_request && e.message.include?("Invalid account number format")
  account.update!(sync_disabled: true, disable_reason: "bad_account_number")
end

Prevention

When it happens

Trigger: Passing nil/blank; passing an internal UUID or IBAN from another provider's account record; whitespace or a trailing newline in the stored account number; a hyphenated or formatted code copied from a statement PDF.

Common situations: Mapping accounts between providers by the wrong identifier, account_number column polluted with display names or whitespace from an import, calling get_holdings with the account 'name' instead of the 'account_number' field returned by list_accounts.

Related errors


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