we-promise/sure · error · Provider::Up::UpError

configuration_error

configuration_error

Error message

Up access token is required

What it means

Provider::Up's constructor strips the supplied personal access token and immediately raises UpError with error_type=:configuration_error when it is blank. This is a fail-fast guard: the client refuses to make any HTTP call (such as GET /util/ping) without credentials, so the error is always raised locally, not by Up's API.

Source

Thrown at app/models/provider/up.rb:20

  include HTTParty
  extend SslConfigurable

  DEFAULT_BASE_URL = "https://api.up.com.au/api/v1".freeze
  DEFAULT_PAGE_SIZE = 100
  # Host that authenticated requests (bearer token) may be sent to. Absolute URLs
  # taken from API responses (links.next) are validated against this.
  ALLOWED_HOST = URI.parse(DEFAULT_BASE_URL).host.freeze

  headers "User-Agent" => "Sure Finance Up Client"
  default_options.merge!({ timeout: 120 }.merge(httparty_ssl_options))

  attr_reader :access_token

  # Build a client with the family's Up personal access token. Raises if blank.
  def initialize(access_token)
    @access_token = access_token.to_s.strip

    raise UpError.new("Up access token is required", :configuration_error) if @access_token.blank?
  end

  # GET /util/ping - validates the personal access token.
  # Returns the parsed payload (contains meta.id / meta.statusEmoji) or raises UpError.
  def ping
    get("util/ping")
  end

  # GET /accounts - returns an array of flattened account hashes.
  # Each hash: { id:, displayName:, accountType:, ownershipType:, balance: {...}, createdAt: }
  def get_accounts
    fetch_all_resources("accounts").map { |resource| flatten_account(resource) }
  end

  # GET /accounts/{id}/transactions - returns an array of flattened transaction hashes.
  # Both HELD (pending) and SETTLED (posted) transactions are returned; callers derive
  # pending status from the :status field.
  def get_account_transactions(account_id:, since: nil, until_date: nil, page_size: DEFAULT_PAGE_SIZE)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Ensure every UpItem persists a non-empty up_access_token before enqueueing syncs (validate presence at creation)
  2. Scope sync jobs to items where the token is present: UpItem.where.not(access_token: nil)
  3. If tokens vanished after a deploy, check ACTIVE_RECORD_ENCRYPTION_* env vars match those used at write time
  4. Call provider.ping after construction to validate the token against Up's API before long imports

Example fix

# before
UpItem.find_each { |item| Provider::Up.new(item.access_token).get_accounts }

# after - skip/flag unconfigured items instead of raising
UpItem.find_each do |item|
  token = item.access_token.to_s.strip
  next item.update!(status: "requires_update") if token.blank?
  Provider::Up.new(token).get_accounts
end
Defensive patterns

Strategy: validation

Validate before calling

token = item.access_token.to_s.strip
raise ArgumentError, "Up access token missing for UpItem #{item.id}" if token.blank?
provider = Provider::Up.new(token)

Type guard

def up_configured?(item)
  item.access_token.to_s.strip.present?
end

Try / catch

begin
  Provider::Up.new(token).ping
rescue Provider::Up::UpError => e
  raise if e.error_type != :configuration_error
  item.update!(status: "requires_update") # flag for user re-entry, no HTTP happened
end

Prevention

When it happens

Trigger: Provider::Up.new(nil), Provider::Up.new("") or Provider::Up.new(" ") - i.e. constructing the client from an UpItem whose stored token is empty; token attribute nil because the record was created without one or encryption misconfiguration returned nil.

Common situations: Sync job iterating UpItems including half-configured ones; ActiveRecord Encryption env keys missing/different from those used when the token was written, so the ciphertext cannot be decrypted and the attribute reads blank; UI flow that creates the UpItem before the user pastes the token; tests using factories without the token attribute.

Related errors


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