Incident: tested cards disappear from the UI after edit (GET /api/v1/tamiyo-scroll/card-tests)¶
Status tracking¶
| Field | Value |
|---|---|
| Status | Open |
| Severity | Medium |
| Reported | 2026-07-16 |
| Area | Tamiyo Scroll — "Cartes testées" (BO3 tracking) |
| Blocking | feat/v1-app merge to production |
| Owner | Backend (Agent 1) |
| Workaround in place | Yes — schema relaxed client-side, see below |
Summary¶
After editing a tested card (changing the opponent's deck), the entire list of
tested cards disappears from the UI. Root cause: the frontend is being migrated
to an API contract where every tested card carries a personal_deck_id (uuid,
required), but the GET /api/v1/tamiyo-scroll/card-tests endpoint doesn't
return that field on existing records yet. The client's strict schema validation
then rejects the whole response.
Contract expected on the frontend side¶
cardTestSchema (src/schemas/tamiyoScroll.ts) now expects each item in the
list to look like:
{
id: uuid,
tester: string,
card_name: string,
personal_deck_id: uuid, // new field, required
opponent_deck_id: uuid | null,
rating: int,
notes: string | null,
// ...other existing fields (created_at, etc.)
}
cardTestWriteSchema (POST/PUT) also expects personal_deck_id (uuid, required)
in the write payload.
Concrete payloads¶
GET /api/v1/tamiyo-scroll/card-tests — current (broken) response¶
[
{
"id": "b1c2a3d4-1111-4a2b-9c3d-abcdef123456",
"tester": "Alice",
"card_name": "Goblin Guide",
"opponent_deck_id": "4c3d5ea1-548c-43f5-a552-93f9a2f89e54",
"rating": 4,
"notes": "Great in the current shell",
"created_at": "2026-06-01T10:00:00Z"
}
]
→ personal_deck_id is missing. cardTestSchema.array().parse(...) fails on
this very first element, which invalidates the entire response on the client,
not just this one record.
GET /api/v1/tamiyo-scroll/card-tests — expected (fixed) response¶
[
{
"id": "b1c2a3d4-1111-4a2b-9c3d-abcdef123456",
"tester": "Alice",
"card_name": "Goblin Guide",
"personal_deck_id": "07757e5b-c1cb-4d2a-98da-972deafdfc92",
"opponent_deck_id": "4c3d5ea1-548c-43f5-a552-93f9a2f89e54",
"rating": 4,
"notes": "Great in the current shell",
"created_at": "2026-06-01T10:00:00Z"
}
]
POST / PUT /api/v1/tamiyo-scroll/card-tests(/:id)¶
Write payload sent by the frontend
{
"personal_deck_id": "07757e5b-c1cb-4d2a-98da-972deafdfc92",
"tester": "Alice",
"card_name": "Goblin Guide",
"opponent_deck_id": "4c3d5ea1-548c-43f5-a552-93f9a2f89e54",
"rating": 4,
"notes": "Great in the current shell"
}
This payload is already being sent by the current frontend (toWrite() in
CardTestsSection.tsx builds personal_deck_id from the active personal deck).
The backend needs to:
- accept it on write (create and update),
- persist it,
- return it on every subsequent read — including for rows created before the
- field was introduced (backfill required).
Error observed on the client (Zod, illustrative)¶
ZodError: [
{
code: 'invalid_type',
expected: 'string',
received: 'undefined',
path: [0, 'personal_deck_id'],
message: 'Required',
},
]
This error surfaces at schema.parse(await response.json())
(src/api/client.ts:126) and fails the entire card-tests React Query query.
Reproduction¶
- Open the "Suivi BO3" tab → "Cartes testées" section.
- Edit an existing tested card (e.g. change the opponent's deck) and save.
- The save (PUT) succeeds, which invalidates and fetches
GET /card-testsagain. - If that endpoint's response contains cards without
personal_deck_id(or with a non-uuid value), Zod validation fails on the whole array, not just the edited row. - The component has no explicit error handling on this query, so the list silently falls back to "Aucun retour de test." instead of showing an error.
Impact¶
- All tested cards become invisible in the UI as soon as a single record doesn't match the new contract (not just the one that was edited).
- No data loss on the backend side as far as we know — this is a client-side parsing failure, not a deletion. But from the user's perspective, the perceived effect is a total loss of visibility.
What's expected from the backend¶
GET /api/v1/tamiyo-scroll/card-testsmust returnpersonal_deck_id(uuid) for every tested card, including records created before the field existed (backfill needed if the field wasn't present in the database yet).POST/PUT /api/v1/tamiyo-scroll/card-tests/:idmust accept and persistpersonal_deck_idin the write payload.- Confirm whether
personal_deck_idshould be aligned with the user's active personal deck at the time of the test, and how to handle historical tests with no associated personal deck (default value? explicit migration?).
Code references (frontend, for context)¶
src/schemas/tamiyoScroll.ts—cardTestSchema/cardTestWriteSchemaschemassrc/api/client.ts:126— where the Zod.parse()fails and takes down the whole requestsrc/hooks/useCardTests.ts:30-37—card-testsquery invalidation after update, which triggers the refetch that reveals the problemsrc/pages/suivi-bo3/CardTestsSection.tsx:75— noisErrorhandling on the query, hence the silent failure
Proposed severity — Medium¶
Blocking for the "Cartes testées" feature until the backend returns/accepts
personal_deck_id on this endpoint — to be addressed before merging the
feat/v1-app branch to production.
In the meantime, a workaround is in place and will need to be removed once the fix ships:
export const cardTestSchema = z.object({
id: z.uuid(),
tester: z.string(),
card_name: z.string(),
- personal_deck_id: z.uuid(),
+ personal_deck_id: z.uuid().nullable().optional(),
opponent_deck_id: z.uuid().nullable(),
rating: z.number().int(),
notes: z.string().nullable(),
created_at: z.iso.datetime({ offset: true }),
})