The (int?) + ?? -1 is the relational idiom for "empty → null". But this runs on the MongoDB EF Core provider, where MaxAsync over an empty sequence throwsInvalidOperationException("Sequence contains no elements") instead of returning null. When the restarted stage has no prior run — restarting from a stage that failed before producing a run, or that never ran — the query is empty → throw. ArtStudioController.RestartFromStage catches InvalidOperationException and returns it as 400 BadRequest(ex.Message), so the raw provider message reaches the browser.
Fix
Materialize + Max in memory with an explicit empty fallback (provider-agnostic; retry counts per stage are few and restart is rare). Proven with an integration test that pins the Mongo behavior (the buggy MaxAsync throws; the fixed pattern returns -1).
PR incoming.
Found via the newly-online integration suite running on an Apple-Silicon dev host (#444).
## Symptom (production, learn.spikersoft.com)
Staff clicking to restart the **modeling** stage of an art asset gets an instant **400** with body `Sequence contains no elements` — no run is dispatched.
```
POST https://api.spikersoft.com/api/artstudio/{id}/restart/modeling → 400
```
## Root cause
`RestartFromStageCommandHandler` computes the new run's retry count as `MAX(RetryCount)` over the asset's **prior runs for the restarted stage**:
```csharp
var previousRetryCount = await context.ArtAssetStageRuns
.Where(r => r.AssetId == request.AssetId && r.Stage == request.Stage)
.Select(r => (int?)r.RetryCount)
.MaxAsync(cancellationToken) ?? -1;
```
The `(int?)` + `?? -1` is the **relational** idiom for "empty → null". But this runs on the **MongoDB EF Core provider**, where `MaxAsync` over an **empty** sequence **throws** `InvalidOperationException("Sequence contains no elements")` instead of returning null. When the restarted stage has **no prior run** — restarting from a stage that failed before producing a run, or that never ran — the query is empty → throw. `ArtStudioController.RestartFromStage` catches `InvalidOperationException` and returns it as `400 BadRequest(ex.Message)`, so the raw provider message reaches the browser.
## Fix
Materialize + `Max` in memory with an explicit empty fallback (provider-agnostic; retry counts per stage are few and restart is rare). Proven with an integration test that pins the Mongo behavior (the buggy `MaxAsync` throws; the fixed pattern returns `-1`).
PR incoming.
Found via the newly-online integration suite running on an Apple-Silicon dev host (#444).
Fixed and merged to master in commit 7c6271c (present on current master 1638bf0). RestartFromStageCommandHandler now materializes the prior retry counts with ToListAsync and takes priorRetryCounts.Count > 0 ? priorRetryCounts.Max() : -1 — provider-agnostic, no MaxAsync-on-empty throw. The pinning integration test RestartFromStageRetryCountTests ships alongside it. Closing.
Fixed and merged to `master` in commit `7c6271c` (present on current master `1638bf0`). `RestartFromStageCommandHandler` now materializes the prior retry counts with `ToListAsync` and takes `priorRetryCounts.Count > 0 ? priorRetryCounts.Max() : -1` — provider-agnostic, no `MaxAsync`-on-empty throw. The pinning integration test `RestartFromStageRetryCountTests` ships alongside it. Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptom (production, learn.spikersoft.com)
Staff clicking to restart the modeling stage of an art asset gets an instant 400 with body
Sequence contains no elements— no run is dispatched.Root cause
RestartFromStageCommandHandlercomputes the new run's retry count asMAX(RetryCount)over the asset's prior runs for the restarted stage:The
(int?)+?? -1is the relational idiom for "empty → null". But this runs on the MongoDB EF Core provider, whereMaxAsyncover an empty sequence throwsInvalidOperationException("Sequence contains no elements")instead of returning null. When the restarted stage has no prior run — restarting from a stage that failed before producing a run, or that never ran — the query is empty → throw.ArtStudioController.RestartFromStagecatchesInvalidOperationExceptionand returns it as400 BadRequest(ex.Message), so the raw provider message reaches the browser.Fix
Materialize +
Maxin memory with an explicit empty fallback (provider-agnostic; retry counts per stage are few and restart is rare). Proven with an integration test that pins the Mongo behavior (the buggyMaxAsyncthrows; the fixed pattern returns-1).PR incoming.
Found via the newly-online integration suite running on an Apple-Silicon dev host (#444).
Fixed and merged to
masterin commit7c6271c(present on current master1638bf0).RestartFromStageCommandHandlernow materializes the prior retry counts withToListAsyncand takespriorRetryCounts.Count > 0 ? priorRetryCounts.Max() : -1— provider-agnostic, noMaxAsync-on-empty throw. The pinning integration testRestartFromStageRetryCountTestsships alongside it. Closing.