we-promise/sure · error · ActiveRecord::RecordNotFound

not_found

not_found

Error message

Transaction not found

What it means

Api::V1::TransactionsController#set_transaction (app/controllers/api/v1/transactions_controller.rb:197-209) looks up the transaction through family.transactions joined to entry → account merged with Account.accessible_by(owner). It rescues ActiveRecord::RecordNotFound itself and renders 404 { error: 'not_found', message: 'Transaction not found' } — a direct render, not a re-raise. Non-UUID ids, missing rows, and transactions on accounts the owner cannot access all take this path.

Source

Thrown at app/controllers/api/v1/transactions_controller.rb:204

    render json: {
      message: "Transaction deleted successfully"
    }, status: :ok

  rescue => e
    Rails.logger.error "TransactionsController#destroy error: #{e.message}"
    Rails.logger.error e.backtrace.join("\n")

    render json: {
      error: "internal_server_error",
      message: "An unexpected error occurred"
    }, status: :internal_server_error
  end

  private

    def set_transaction
      raise ActiveRecord::RecordNotFound unless valid_uuid?(params[:id])

      family = current_resource_owner.family
      @transaction = family.transactions
        .joins(entry: :account)
        .merge(Account.accessible_by(current_resource_owner))
        .find(params[:id])
      @entry = @transaction.entry
    rescue ActiveRecord::RecordNotFound
      render json: {
        error: "not_found",
        message: "Transaction not found"
      }, status: :not_found
    end

    def ensure_read_scope
      authorize_scope!(:read)
    end

View on GitHub (pinned to e69894adb9)

Solutions

  1. List with GET /api/v1/transactions and use a current id
  2. Confirm the account is still accessible to the token owner (not revoked/archived)
  3. Validate UUID format before the call
  4. On bulk operations, tolerate 404s for rows removed by dedupe rather than aborting the batch
Defensive patterns

Strategy: try-catch

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
UUID_RE.match?(id) or raise ArgumentError, 'transaction id must be a UUID'

Try / catch

begin
  client.get("/api/v1/transactions/#{id}")
rescue Faraday::ResourceNotFound
  # 404 { error: 'not_found', message: 'Transaction not found' }
end

Prevention

When it happens

Trigger: GET/PATCH/DELETE /api/v1/transactions/:id with a non-UUID; a deleted transaction; a transaction whose account was revoked from the owner (no longer accessible_by) — for example a formerly shared account.

Common situations: Access to a shared account was revoked, so history that used to be fetchable now 404s; bulk importers retrying after transactions were merged/deleted by duplicate detection; stale ids in email-deep-links.

Related errors


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