dotnet/eShop · error · RpcException

NotFound

NotFound

Error message

Basket with buyer id {userId} does not exist

What it means

Thrown by BasketService.UpdateBasket via ThrowBasketDoesNotExist when IBasketRepository.UpdateBasketAsync returns null, i.e. the buyer's basket key was not found in the backing store (Redis). It is an RpcException with gRPC StatusCode.NotFound, signalling the client referenced a basket that does not exist. Note the exception is only raised from the UpdateBasket path; DeleteBasket silently no-ops on a missing basket.

Source

Thrown at src/Basket.API/Grpc/BasketService.cs:75

    }

    public override async Task<DeleteBasketResponse> DeleteBasket(DeleteBasketRequest request, ServerCallContext context)
    {
        var userId = context.GetUserIdentity();
        if (string.IsNullOrEmpty(userId))
        {
            ThrowNotAuthenticated();
        }

        await repository.DeleteBasketAsync(userId);
        return new();
    }

    [DoesNotReturn]
    private static void ThrowNotAuthenticated() => throw new RpcException(new Status(StatusCode.Unauthenticated, "The caller is not authenticated."));

    [DoesNotReturn]
    private static void ThrowBasketDoesNotExist(string userId) => throw new RpcException(new Status(StatusCode.NotFound, $"Basket with buyer id {userId} does not exist"));

    private static CustomerBasketResponse MapToCustomerBasketResponse(CustomerBasket customerBasket)
    {
        var response = new CustomerBasketResponse();

        foreach (var item in customerBasket.Items)
        {
            response.Items.Add(new BasketItem()
            {
                ProductId = item.ProductId,
                Quantity = item.Quantity,
            });
        }

        return response;
    }

    private static CustomerBasket MapToCustomerBasket(string userId, UpdateBasketRequest customerBasketRequest)

View on GitHub (pinned to 9b4f9434f4)

Solutions

  1. Call GetBasket first; if it returns empty, the basket does not exist yet — populate items and call UpdateBasket only after a basket exists (or rely on UpdateBasketAsync to create when the repository implementation supports upsert).
  2. Verify the Redis connection string and that the Redis data volume persists across container restarts.
  3. Confirm the buyerId supplied (from the token) matches the one used when the basket was originally stored.
  4. Handle the NotFound RpcException in the client and fall back to creating a fresh basket for the buyer.

Example fix

// before
var resp = await basketClient.UpdateBasketAsync(new UpdateBasketRequest { /* items */ });

// after
try {
    var resp = await basketClient.UpdateBasketAsync(req, headers);
} catch (RpcException ex) when (ex.StatusCode == StatusCode.NotFound) {
    // basket missing — re-initialise for this buyer then retry update
    await basketClient.UpdateBasketAsync(req, headers);
}
Defensive patterns

Strategy: try-catch

Validate before calling

var existing = await basketClient.GetBasketAsync(new GetBasketRequest(), headers);
var exists = existing.Items.Count > 0 || /* or a server flag */ true; // see repository semantics
// If your repository exposes an Exists check, call it before Update.

Type guard

static bool BasketLooksPresent(CustomerBasketResponse resp) => resp is not null; // empty response == not found in this API

Try / catch

try {
    return await basketClient.UpdateBasketAsync(req, headers);
} catch (RpcException ex) when (ex.StatusCode == StatusCode.NotFound) {
    // basket missing: create a fresh basket for the buyer, then retry the update
    return await basketClient.UpdateBasketAsync(req, headers);
}

Prevention

When it happens

Trigger: Calling UpdateBasket for a buyerId whose basket was never created, was already deleted, or expired from Redis before the update. Also reproduced if the Redis connection/data volume is non-persistent across restarts so previously created baskets vanish, or if the buyerId used by the client differs from the one under which the basket was stored.

Common situations: First basket action for a new user is Update instead of an initial Create/Get flow; Redis container lost its volume (dev/test restart); misconfigured Redis connection string pointing at a different instance; the IdentityServer-issued sub claim changed for the same human (re-issued identity) so the key no longer matches.

Related errors


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