{"record":{"id":"e9f91b51674d4bd5","repo":"we-promise/sure","slug":"pagination-error","errorCode":"pagination_error","errorMessage":"Brex pagination exceeded #{MAX_PAGES} pages","messagePattern":"Brex pagination exceeded #(.+?) pages","errorType":"exception","errorClass":"Provider::Brex::BrexError","httpStatus":null,"severity":"error","filePath":"app/models/provider/brex.rb","lineNumber":134,"sourceCode":"\n      { amount: total, currency: currency }\n    end\n\n    def posted_at_start_params(start_date)\n      return {} if start_date.blank?\n\n      { posted_at_start: rfc3339_start_date(start_date) }\n    end\n\n    def get_paginated(path, params: {})\n      records = []\n      cursor = nil\n      seen_cursors = Set.new\n      page_count = 0\n\n      loop do\n        page_count += 1\n        raise BrexError.new(\"Brex pagination exceeded #{MAX_PAGES} pages\", :pagination_error) if page_count > MAX_PAGES\n\n        page_params = params.compact.merge(limit: DEFAULT_LIMIT)\n        page_params[:cursor] = cursor if cursor.present?\n\n        response_payload = get_json(path, params: page_params)\n        if response_payload.is_a?(Array)\n          records.concat(response_payload)\n          break\n        end\n\n        page_records = extract_records(response_payload)\n        records.concat(page_records)\n\n        next_cursor = response_payload.with_indifferent_access[:next_cursor]\n        break if next_cursor.blank?\n\n        if seen_cursors.include?(next_cursor)\n          raise BrexError.new(\"Brex pagination returned a repeated cursor\", :pagination_error)","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/brex.rb#L116-L152","documentation":"StandardError raised by SnaptradeItem#import_latest_snaptrade_data when snaptrade_provider (resolved via SnaptradeItem::Provided, returning nil when OAuth credentials are absent) is nil. The item model logs 'OAuth not authorized' then raises with the generic user-facing message; the rescue re-logs and re-raises. In practice this means oauth_access_token is missing — the SnapTrade brokerage link was never completed or its tokens were cleared.","triggerScenarios":"A sync job running for a SnaptradeItem whose OAuth flow never finished (user abandoned the connection portal); tokens revoked/deleted; item destroyed but a queued sync job still executes; calling import_latest_snaptrade_data directly in console/rake for an unlinked item.","commonSituations":"User starts SnapTrade linking, closes the portal mid-flow, and a welcome/initial sync fires anyway; cleanup of OAuth tokens without cancelling scheduled jobs; staging seeds creating items without auth.","solutions":["Complete SnapTrade authorization (connection portal flow) so oauth_access_token is stored","Verify item.oauth_configured? (oauth_access_token.present?) before starting the sync job","Cancel or skip scheduled jobs for items with missing OAuth tokens","If tokens were intentionally revoked, destroy the item (destroy_later) instead of leaving it syncable"],"exampleFix":"# before\nSnaptradeItem.find(id).import_latest_snaptrade_data(sync: sync)\n\n# after\nitem = SnaptradeItem.find(id)\nunless item.oauth_configured?\n  Rails.logger.info(\"Skipping SnaptradeItem #{id}: OAuth not authorized\")\n  next # or return\nend\nitem.import_latest_snaptrade_data(sync: sync)","handlingStrategy":"validation","validationCode":"return unless item.oauth_configured? # oauth_access_token.present?","typeGuard":null,"tryCatchPattern":"begin\n  item.import_latest_snaptrade_data(sync: sync)\nrescue StandardError => e\n  raise unless e.message == \"SnapTrade is not authorized\"\n  sync.update!(status: :error, status_text: \"SnapTrade not connected — reconnect required\")\nend","preventionTips":["Check oauth_configured? before enqueueing or running sync jobs","Cancel scheduled jobs when OAuth tokens are revoked or item is destroyed","Complete the SnapTrade authorization flow before exposing sync actions in the UI"],"tags":["snaptrade","oauth","provider-not-configured","sync","brokerage"],"backgroundTag":"provider-not-configured","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}