we-promise/sure · warning · ApiError

IBKR Flex statement is still being generated.

Error message

IBKR Flex statement is still being generated.

What it means

IBKR generates Flex statements asynchronously: SendRequest queues the report, GetStatement returns ErrorCode 1004/1019 while it is still being produced. poll_statement sleeps POLL_INTERVAL=3s and retries up to MAX_POLL_ATTEMPTS=20 (~60s total); if the statement still is not ready, this ApiError (with the pending error_code attached) is raised.

Source

Thrown at app/models/provider/ibkr_flex.rb:86

      reference_code
    end

    def poll_statement(reference_code)
      attempts = 0

      loop do
        attempts += 1
        response = with_retries("GetStatement") do
          self.class.get("/GetStatement", query: { t: token, q: reference_code, v: 3 })
        end

        xml = parse_xml(response.body)
        return response.body if xml.at_xpath("//FlexQueryResponse")

        error = response_error(xml, response)
        if error.is_a?(ApiError) && PENDING_ERROR_CODES.include?(error.error_code.to_s)
          raise ApiError.new("IBKR Flex statement is still being generated.", error_code: error.error_code) if attempts >= MAX_POLL_ATTEMPTS

          sleep(POLL_INTERVAL)
          next
        end

        raise(error || ApiError.new("IBKR Flex returned an unexpected response.", status_code: response.code, response_body: response.body))
      end
    end

    def response_error(xml, response)
      error_code = xml.at_xpath("//ErrorCode")&.text.to_s.strip.presence
      error_message = xml.at_xpath("//ErrorMessage")&.text.to_s.strip.presence

      return nil if error_code.blank? && response.success?

      message = error_message.presence || "IBKR Flex request failed"

      case error_code

View on GitHub (pinned to e69894adb9)

Solutions

  1. Retry download_statement later (schedule a second pass in a few minutes) - generation usually completes on its own
  2. Shrink the Flex Query: shorter date range, fewer sections, split into multiple queries
  3. If statements routinely exceed 60s for your accounts, raise MAX_POLL_ATTEMPTS or POLL_INTERVAL - but note the sleep blocks the worker thread, so prefer a background job with a longer budget
  4. Keep error.error_code (1004/1019) attached when surfacing, so callers can distinguish 'still pending' from hard failures

Example fix

# caller - the statement usually finishes if you come back later
# before
statement = flex.download_statement

# after
begin
  statement = flex.download_statement
rescue Provider::IbkrFlex::ApiError => e
  raise unless %w[1004 1019].include?(e.error_code.to_s)
  FlexDownloadJob.perform_later(account, wait: 5.minutes)
  raise
end
Defensive patterns

Strategy: retry

Type guard

def ibkr_statement_pending?(error)
  error.is_a?(Provider::IbkrFlex::ApiError) &&
    %w[1004 1019].include?(error.error_code.to_s)
end

Try / catch

begin
  statement = flex.download_statement
rescue Provider::IbkrFlex::ApiError => e
  raise unless %w[1004 1019].include?(e.error_code.to_s)
  raise if attempts >= 2 # give up after 2 reschedules
  FlexDownloadJob.perform_later(account, wait: 5.minutes)
end

Prevention

When it happens

Trigger: A Flex Query covering years of data and many sections (trades, transfers, positions...) on a large account; IBKR backend slowness at month-end or during statement cycles; first-ever run of a newly created query that has to generate a long backfill.

Common situations: Accounts with heavy trading history, date ranges spanning years, queries including multiple report sections, IBKR data-center congestion - generation simply takes longer than the built-in ~60s budget.

Related errors


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