[Bug][Backend] Zero-question quizzes are marked Completed and takeable — starting one insta-expires at 0/0 #507

Closed
opened 2026-07-12 17:36:13 +00:00 by spikerj · 3 comments
Owner

Symptom (prod, 2026-07-12 17:34 UTC)

Taking a generated quiz: UI shows started, then immediately 'time expired' and ends. Seq shows the whole attempt lasted 3 seconds:

17:34:43  StartQuizAttemptCommand completed (30ms)
17:34:46  Completing quiz attempt 6a53d033a41ad80628b1667a
          Completed quiz attempt: 0/0 (0%)

Root cause chain (two layers)

1. Generation marks empty quizzes as Completed/available. Quiz 6a53cdfdc31783225d731df6 (sample-PDF book 6a53cdfdc31783225d731df5) finished as:

⏭️ Page 4/6 not found in database, skipping (may have failed during extraction)   (pages 4–6)
Quiz generation completed ... Total questions: 0
Sent BookProcessingNotification: Stage=Completed

Zero questions should be a FAILED/flagged generation, not Completed — and the page-count mismatch (quiz believed 6 pages; extraction stored 3 for this book) deserves its own look: the same sample PDF (identical SHA256) was uploaded several times tonight; the page expectations appear to have come from a different upload's record.

2. The attempt flow lets an empty quiz start. StartQuizAttemptCommand happily creates an attempt against Questions.Count == 0, and the player degenerates to instant expiry + auto-complete 0/0.

Proposed fixes

  • QuizGeneration: if total questions == 0 → mark quiz Failed (ErrorMessage='no questions generated'), do NOT surface as takeable; notification severity Warning not success.
  • API: StartQuizAttemptCommand validator rejects quizzes with no questions (422) — belt for whatever braces miss.
  • Investigate the page-count source for the mismatch (likely cross-upload contamination via duplicate-SHA books).
  • Frontend (spikersoft-angular): show 'quiz unavailable' state instead of starting an empty quiz — minor, backend guards make it unreachable.

Healthy-path control: quiz 6a53cff5c31783225d731dfc generated 'Total questions: 2' at 17:33:53 the same evening — generation itself works on books with adequate content.

Related: tonight's pipeline work #497/#500; the affected book was a thin 3-page sample PDF.

## Symptom (prod, 2026-07-12 17:34 UTC) Taking a generated quiz: UI shows started, then immediately 'time expired' and ends. Seq shows the whole attempt lasted 3 seconds: 17:34:43 StartQuizAttemptCommand completed (30ms) 17:34:46 Completing quiz attempt 6a53d033a41ad80628b1667a Completed quiz attempt: 0/0 (0%) ## Root cause chain (two layers) **1. Generation marks empty quizzes as Completed/available.** Quiz 6a53cdfdc31783225d731df6 (sample-PDF book 6a53cdfdc31783225d731df5) finished as: ⏭️ Page 4/6 not found in database, skipping (may have failed during extraction) (pages 4–6) Quiz generation completed ... Total questions: 0 Sent BookProcessingNotification: Stage=Completed Zero questions should be a FAILED/flagged generation, not Completed — and the page-count mismatch (quiz believed 6 pages; extraction stored 3 for this book) deserves its own look: the same sample PDF (identical SHA256) was uploaded several times tonight; the page expectations appear to have come from a different upload's record. **2. The attempt flow lets an empty quiz start.** StartQuizAttemptCommand happily creates an attempt against Questions.Count == 0, and the player degenerates to instant expiry + auto-complete 0/0. ## Proposed fixes - QuizGeneration: if total questions == 0 → mark quiz Failed (ErrorMessage='no questions generated'), do NOT surface as takeable; notification severity Warning not success. - API: StartQuizAttemptCommand validator rejects quizzes with no questions (422) — belt for whatever braces miss. - Investigate the page-count source for the mismatch (likely cross-upload contamination via duplicate-SHA books). - Frontend (spikersoft-angular): show 'quiz unavailable' state instead of starting an empty quiz — minor, backend guards make it unreachable. Healthy-path control: quiz 6a53cff5c31783225d731dfc generated 'Total questions: 2' at 17:33:53 the same evening — generation itself works on books with adequate content. Related: tonight's pipeline work #497/#500; the affected book was a thin 3-page sample PDF.
Author
Owner

Fix up in spikersoft-backend PR #231 (worker Failed-state + StartQuizAttempt guard + 400 mapping + 3 xUnit tests — details on the PR). The page-count-mismatch investigation (pages 4–6 'not found', suspected duplicate-SHA cross-contamination) is deliberately NOT in this PR — keeping this ticket's scope to the takeable-empty-quiz defect; happy to spin the mismatch into its own ticket if it reproduces on distinct files.

Fix up in spikersoft-backend PR #231 (worker Failed-state + StartQuizAttempt guard + 400 mapping + 3 xUnit tests — details on the PR). The page-count-mismatch investigation (pages 4–6 'not found', suspected duplicate-SHA cross-contamination) is deliberately NOT in this PR — keeping this ticket's scope to the takeable-empty-quiz defect; happy to spin the mismatch into its own ticket if it reproduces on distinct files.
Author
Owner

Second code layer landed: PR #233 — the true mechanism of the zero-question quiz was the fixed pagesToSkip=3 making a 3-page book start at page 4 (its whole content skipped). New ComputePageWindow skips min(3, half-the-book); 11 window theories in a new QuizGeneration.Tests project. With #231 (fail-state + start guard) + #233 (window), the user-facing defect is fully covered. The remaining page-count-inflation anomaly (6 pages on a 3-pager; one book linking 570 pages) is now #509 with tonight's evidence — it needs Mongo inspection. Suggest closing this ticket once #231+#233 merge; #509 carries the data thread.

Second code layer landed: **PR #233** — the true mechanism of the zero-question quiz was the fixed `pagesToSkip=3` making a 3-page book start at page 4 (its whole content skipped). New `ComputePageWindow` skips min(3, half-the-book); 11 window theories in a new QuizGeneration.Tests project. With #231 (fail-state + start guard) + #233 (window), the user-facing defect is fully covered. The remaining page-count-inflation anomaly (6 pages on a 3-pager; one book linking 570 pages) is now **#509** with tonight's evidence — it needs Mongo inspection. Suggest closing this ticket once #231+#233 merge; #509 carries the data thread.
Author
Owner

Fixed across three merged PRs and deployed via CI: #231 (0-question generations marked Failed + StartQuizAttempt guard + 400 mapping), #233 (page window no longer skips past small books — the true zero-question mechanism), #234 (the duplicate-upload page-merge that inflated the count in the first place). The broken UI state is unreachable: Failed quizzes leave the list, empty starts are rejected server-side, small books quiz their real content. Closing.

Fixed across three merged PRs and deployed via CI: #231 (0-question generations marked Failed + StartQuizAttempt guard + 400 mapping), #233 (page window no longer skips past small books — the true zero-question mechanism), #234 (the duplicate-upload page-merge that inflated the count in the first place). The broken UI state is unreachable: Failed quizzes leave the list, empty starts are rejected server-side, small books quiz their real content. Closing.
Sign in to join this conversation.