we-promise/sure · warning · Api::V1::ValuationsController::InvalidFilterError

validation_failed

validation_failed

Error message

account_id must be a valid UUID

What it means

Api::V1::ValuationsController#apply_filters (app/controllers/api/v1/valuations_controller.rb:256-259) validates the account_id query param with valid_uuid? before filtering. A present but malformed account_id raises InvalidFilterError 'account_id must be a valid UUID', rescued into a 422 validation_failed response. Format only — existence is not checked.

Source

Thrown at app/controllers/api/v1/valuations_controller.rb:257

      @valuation = @entry.entryable
    rescue ActiveRecord::RecordNotFound
      render json: {
        error: "not_found",
        message: "Valuation not found"
      }, status: :not_found
    end

    def ensure_read_scope
      authorize_scope!(:read)
    end

    def ensure_write_scope
      authorize_scope!(:write)
    end

    def apply_filters(query)
      if params[:account_id].present?
        raise InvalidFilterError, "account_id must be a valid UUID" unless valid_uuid?(params[:account_id])

        query = query.where(account_id: params[:account_id])
      end
      query = query.where("entries.date >= ?", parse_date_param(:start_date)) if params[:start_date].present?
      query = query.where("entries.date <= ?", parse_date_param(:end_date)) if params[:end_date].present?
      query
    end

    def parse_date_param(key)
      Date.iso8601(params[key].to_s)
    rescue ArgumentError
      raise InvalidFilterError, "#{key} must be an ISO 8601 date"
    end

    def safe_page_param
      page = params[:page].to_i
      page > 0 ? page : 1
    end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Send account_id as a canonical UUID from GET /api/v1/accounts
  2. Validate UUID format client-side before the request
  3. Remember valid-but-unknown account ids yield empty data, not errors — verify against the accounts list if results look wrong
  4. URL-encode the param; avoid curly quotes from rich-text copy/paste

Example fix

# before
GET /api/v1/valuations?account_id=checking
# after
GET /api/v1/valuations?account_id=0e8d1ceb-5b6d-4a85-9f2e-74f3b1a2c9d4
Defensive patterns

Strategy: validation

Validate before calling

UUID_RE = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
raise ArgumentError, 'account_id must be a valid UUID' if account_id && !UUID_RE.match?(account_id)

Type guard

def valid_uuid?(v) = v.to_s.match?(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i)

Try / catch

begin
  client.get('/api/v1/valuations', { account_id: aid })
rescue Faraday::UnprocessableEntity => e
  # 422 validation_failed 'account_id must be a valid UUID'
end

Prevention

When it happens

Trigger: GET /api/v1/valuations?account_id=main or ?account_id=123. A well-formed UUID for a nonexistent account returns an empty list, not an error.

Common situations: Passing the account name or legacy numeric id instead of the UUID; copying ids between query and JSON body contexts and losing characters; templates that leave a placeholder like 'ACCOUNT_ID'.

Related errors


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