dotnet/eShop · error · CatalogDomainException

Empty stock, product item {Name} is sold out

Error message

Empty stock, product item {Name} is sold out

What it means

Thrown by CatalogItem.RemoveStock when AvailableStock is exactly zero. RemoveStock is the only sanctioned way to decrement catalog inventory (DDD aggregate behavior); it refuses to operate on a sold-out item rather than returning a partial/zero result. It is a CatalogDomainException, surfaced to callers as a domain rule violation.

Source

Thrown at src/Catalog.API/Model/CatalogItem.cs:66


    /// <summary>
    /// Decrements the quantity of a particular item in inventory and ensures the restockThreshold hasn't
    /// been breached. If so, a RestockRequest is generated in CheckThreshold. 
    /// 
    /// If there is sufficient stock of an item, then the integer returned at the end of this call should be the same as quantityDesired. 
    /// In the event that there is not sufficient stock available, the method will remove whatever stock is available and return that quantity to the client.
    /// In this case, it is the responsibility of the client to determine if the amount that is returned is the same as quantityDesired.
    /// It is invalid to pass in a negative number. 
    /// </summary>
    /// <param name="quantityDesired"></param>
    /// <returns>int: Returns the number actually removed from stock. </returns>
    /// 
    public int RemoveStock(int quantityDesired)
    {
        if (AvailableStock == 0)
        {
            throw new CatalogDomainException($"Empty stock, product item {Name} is sold out");
        }

        if (quantityDesired <= 0)
        {
            throw new CatalogDomainException($"Item units desired should be greater than zero");
        }

        int removed = Math.Min(quantityDesired, this.AvailableStock);

        this.AvailableStock -= removed;

        return removed;
    }

    /// <summary>
    /// Increments the quantity of a particular item in inventory.
    /// <param name="quantity"></param>
    /// <returns>int: Returns the quantity that has been added to stock</returns>

View on GitHub (pinned to 9b4f9434f4)

Solutions

  1. Before calling RemoveStock, check AvailableStock and short-circuit or surface an out-of-stock message to the user when it is 0.
  2. Treat the returned int as authoritative: RemoveStock may return fewer units than requested when stock is partial — if it returns 0, branch to out-of-stock handling instead of retrying blindly.
  3. Use optimistic concurrency (a row version / ETag on CatalogItem) to prevent two writers racing the last unit, and re-read stock on a concurrency conflict before retrying.
  4. Restock via AddStock so AvailableStock is non-zero before further RemoveStock calls.

Example fix

// before
var taken = item.RemoveStock(qty);

// after
if (item.AvailableStock == 0) {
    return Result.OutOfStock(item.Name);
}
var taken = item.RemoveStock(qty);
if (taken < qty) {
    return Result.PartiallyFulfilled(taken);
}
Defensive patterns

Strategy: validation

Validate before calling

if (item.AvailableStock <= 0) {
    return Result.Fail($"{item.Name} is sold out");
}
var taken = item.RemoveStock(qty);

Type guard

static bool HasStock(CatalogItem item) => item.AvailableStock > 0;

Try / catch

try {
    var taken = item.RemoveStock(qty);
} catch (CatalogDomainException ex) when (ex.Message.Contains("sold out")) {
    // surface out-of-stock to the user; do not retry blindly
}

Prevention

When it happens

Trigger: A checkout/stock-reservation flow calls RemoveStock(quantityDesired) for an item whose AvailableStock has already been driven to 0 by prior orders. Also triggered by race conditions where two concurrent reservations drain the last unit, or by stale catalog data read before a previous decrement committed.

Common situations: High-demand/SKU product selling out under load; catalog cache returning an outdated positive stock figure; ordering service processing a backlogged queue after the catalog was depleted; integration tests not reseeding stock between runs.

Related errors


AI-assisted analysis of dotnet/eShop@9b4f9434f4 (2026-08-13). Data as JSON: /api/errors/e84acdcdfd3a2e63. Report an issue: GitHub.