Skip to content

Trace an action to a failure

Follow recorded actions, requests, and errors while keeping causality explicit.

Last reviewed Open Markdown

Start with the tester’s expected and actual result, then trace the retained evidence around the failing action.

Download and extract the real sample bundle. Open the recording folder described in bundle structure. This investigation uses the supplied files directly.

The application in this recording is Quackban, a card board on SpaceDucking. See About Quackban and credits for attribution to Ministry of Testing and the MoTaverse creators.

In session.md, QA expects moving a card out of Blocked to clear its Blocked tag. The recorded path creates QB-DEMO sticky Blocked in Pond Redesign, moves it to Blocked, then to In progress. The session starts at 2026-10-07T04:43:24.309Z and lasts 42 seconds.

The description also mentions Done and a reload in earlier runs. Those are tester observations about other attempts: this recording does not contain those checks.

Action offsets in session.md are relative to the session start. HAR startedDateTime values are absolute UTC times. Add the offset to the start to locate nearby traffic, then confirm the card ID and payload rather than relying on timing alone.

All HAR indexes below are zero-based; Markdown action numbers are one-based.

Evidence Time (UTC, 2026-10-07) What to inspect
Action 17: Clicked Blocked 04:43:49.834 (+25.525s) The first lane change.
log.entries[2]: POST 04:43:49.837 Move payload contains the card ID and status: "blocked".
log.entries[3]: GET 04:43:50.465 Board response after entering Blocked.
Action 20: Clicked In progress 04:43:56.535 (+32.226s) The move that should clear the tag.
log.entries[4]: POST 04:43:56.539 Move payload contains the same ID and status: "in_progress"; no blocked field is sent.
log.entries[5]: GET 04:43:57.141 Board response after leaving Blocked.

The move endpoint ends in ef480cff6458da319ee4e576095f30ac06003e3931bf8be09ee209dea582919f; the board endpoint ends in e4c54e0e02fd6c771bbd5710d32e931705db77533c1189bdcba83ed643a2f8b2. These are recorded application routes, not Recorder APIs or stable endpoint names for other builds.

In the HAR, parse request.postData.text and response.content.text as JSON for these entries. Their bodies use an application serialization format with parallel p.k (keys) and p.v (values) arrays. This wrapper belongs to the captured application, not HAR or Recorder’s Report schema.

For the second move, t.p.v[0].p.k is ["id", "status"] and t.p.v[0].p.v[1].s is "in_progress". The card ID is 3229d3de-fb6d-4196-8f89-f0bf94d63392.

In the last board response, p.v[0].p.v[2].a contains the cards. The sample card is at index 44. Match its ID when inspecting another capture; array positions can change. Within that card, key 4 is status and key 10 is blocked. The status value is {"t":1,"s":"in_progress"} and the blocked value is {"t":2,"s":2}, unchanged from the preceding board response. The initial board response has {"t":2,"s":3} for blocked. QA’s note interprets this transition as false → true → true; use the application’s serializer to decode these tags in application tooling.

Save this TypeScript as inspect-sample.mts in the extracted recording folder. With Node.js 24 or later, run node inspect-sample.mts to print the relevant raw fields without sending any requests:

import { readFileSync } from "node:fs";
type EncodedField = { t: number; s?: string | number };
type EncodedCard = { p: { k: string[]; v: EncodedField[] } };
const { log: { entries } } = JSON.parse(readFileSync("network.har", "utf8"));
const cardId = "3229d3de-fb6d-4196-8f89-f0bf94d63392";
const move = JSON.parse(entries[4].request.postData.text);
console.log("move fields:", JSON.stringify(move.t.p.v[0].p.k));
console.log("new status:", move.t.p.v[0].p.v[1].s);
for (const index of [1, 3, 5]) {
const body = JSON.parse(entries[index].response.content.text);
const cards: EncodedCard[] = body.p.v[0].p.v[2].a;
for (const card of cards) {
const fields = Object.fromEntries(
card.p.k.map((key, i) => [key, card.p.v[i]]),
);
if (fields.id.s === cardId) {
console.log(index, fields.status.s, JSON.stringify(fields.blocked));
}
}
}

Expected output:

move fields: ["id","status"]
new status: in_progress
1 backlog {"t":2,"s":3}
3 blocked {"t":2,"s":2}
5 in_progress {"t":2,"s":2}

This is a reader for this sample’s payload shape, not a general HAR or application-protocol decoder.

screenshots/003-area.png shows the card with a red Blocked badge. Its crop does not show the lane heading; use the action sequence and final board response for the In progress state. 002-full.png shows an earlier state with the card in the Blocked lane.

The evidence supports investigating the lane-change update and the relationship between status and blocked. Check the application contract: should the server clear the flag when a card leaves Blocked, should the client send another field, or can the flag be managed independently? The missing field alone does not decide that design question or prove a database defect.

All six requests returned HTTP 200. No console.log is included, and the summary reports zero console errors. The creation payload is redacted. Record these limits, then inspect application code and add a regression assertion for the agreed behavior.

Locate the action in the numbered Recorded actions section of session.md. Confirm its label, relative offset, and surrounding sequence. Use steps.json or a generated script when you need candidate selectors or converted input values. If the action is outside the retained interval, ask for the missing context or a focused new recording.

Use the action’s relative offset and the session start to locate nearby entries in network.har. Check method, URL, status, payload, response, and timing. Confirm the same subject with an identifier or value before treating the request as related.

Follow related console entries and available stack traces. Keep background requests, polling, and unrelated warnings in view: nearby activity can be coincidental.

Artifact relationships reconstructed from timestamps are leads, not proven causes. The bundle does not preserve the Viewer’s inferred-link control or every association detail.

For example, a save action followed by a 500 response supports investigating that request. It does not by itself establish whether the failure originated in validation, a downstream service, or a separate concurrent operation.

Use backend correlation where configured, or reproduce the request in an appropriate test environment. Compare the evidence with application code and server logs before naming a cause.

Record the failing action, relevant request and error timestamps, confirmed observations, and remaining hypotheses in the issue or investigation record. Cite filenames and HAR entry indexes so another reader can verify the reasoning. Ask the tester for a new notes.md or Description when missing context must be preserved in another bundle.

Missing bodies, redacted values, or absent earlier events limit the conclusion. State those gaps rather than treating missing evidence as proof of absence.