HackBlue

Tools · Web testing tools · 23 / 23

Walkthrough: why Save note loses the text

This pass uses the Network list, Preserve log, the initiator, the request panes, and local storage on one Harbor Ledger bug. The button navigates away before POST /api/notes finishes, and a stale draft can still be sitting in the browser after the server never stored it.

18 min read · practice only with authorization

Set the panel up before the click

Start Harbor Ledger on http://127.0.0.1:3000. Use the lab browser profile, signed in as Avery with the seed account. Open DevTools, Network, and turn Preserve log on. Enable the Initiator column. Clear the list so this attempt is the only thing in it. Open Application storage long enough to see whether harbor.draftNote already has text, and write that down so you do not confuse an old draft with this click.

Pick a sentence you have not used: “checked April retainer.” Type it into the note field on invoice 1042. You are about to click once. Everything after this paragraph is reading, until the confirmation at the end.

  • Origin: http://127.0.0.1:3000
  • Account: Avery
  • Preserve log: on
  • Initiator column: on
  • List: cleared
  • Text: a sentence you can search for

One click, then the rows in order

Click Save note. The address bar ends on /invoices. The Network list, because Preserve log is on, still shows the invoice page’s leftovers only if you forgot to clear. The rows from this click are POST /api/notes with status canceled, then the document GET /invoices with status 200, then the list’s API fetch. If you do not see the POST, you cleared too late or Preserve log was off. Do not interpret the destination page as the capture. Set the checkbox and click again with a new sentence.

Select the POST. The response pane is empty. The payload contains your sentence. The initiator is /static/app.js, in the click handler that also assigns the location. Select the document row and see the same file. You now have the cause in one function: it starts the save and leaves the page.

Check the API and the draft key separately

Reload the list and look at GET /api/invoices in the response pane, or open the note list the UI renders from that response. The sentence is absent. That matches a POST the server may never have seen. Then open Application storage for this origin and read harbor.draftNote. The lab script writes the draft as you type, so the sentence may still be there even though the save was canceled. If it is, the form will show it again when you return to the invoice, which feels like the app “remembered” a save it did not keep.

Write both facts. “The list does not show it” is the clerk’s symptom. “The POST was canceled by our own navigation, and the draft key still has the text” is the diagnosis. They point at app.js, not at the database.

Snippet
POST /api/notes
  (canceled)
  payload contains: checked April retainer
  response pane: empty
  initiator: /static/app.js

GET /api/invoices
  200
  body does not contain that sentence

local storage
  harbor.draftNote may still contain that sentence

Change the handler, then run the same click

In the lab’s app.js, change the click handler so it waits for the POST to finish, navigates to the list only if that succeeded, and removes harbor.draftNote after success. Leave the failure on the invoice page if the POST fails, so a real server error is visible instead of a silent navigation. This is your app, on loopback, and the change is ordinary front-end control flow.

Hard-reload so the browser fetches the new script. Clear the Network list. Type a new sentence. Click once. POST /api/notes should complete with a real status code, initiator still the script, response pane no longer empty. The following navigation can still be initiated by app.js, and that is fine, because it happens after the response. The list’s JSON contains the sentence. The draft key is gone. Put this capture under the first one. The difference is the regression test.

Worked case: the note you can hand to yourself

Fill the bug-note block from this lesson’s diagram with the before-capture and a second copy with the after-capture. A reader who has the lab can repeat it without your session cookie, because the steps sign in as Avery from the seed account and quote paths rather than cookies. A reader who does not have the lab can still see that you separated a canceled request, a script initiator, an empty response, a missing API row, and a local draft.

Stop there. The bug was a race in a click handler and a draft key that outlived the account’s intent. Nothing in the capture asks you to send the note text to any host but the one you are running, and nothing in it gets clearer if you add a scanner. The panels were sufficient.

Diagram

Five checks, one click

  1. Setup

    Lab profile, http://127.0.0.1:3000, signed in as Avery, DevTools open, Preserve log on, list cleared.

  2. Click

    Type a fresh note and click Save note once. The list keeps a canceled POST and then a document load of /invoices.

  3. Cause

    Both rows are initiated by the click handler in /static/app.js. The response pane of the POST is empty.

  4. Stores

    The sentence is absent from the API, and it may still be present in harbor.draftNote. The fix waits for the POST before navigating.

Two stories about the same button

The server lost it

Evidence. The list page does not show the note.

Unused. Preserve log, initiator, and the empty response pane.

Change. You rewrite the API.

The click left early

Evidence. POST /api/notes is canceled. Initiator is app.js. Response pane is empty.

Also. Local storage may still hold the draft. The API list does not.

Change. The handler awaits the POST, then navigates, and the draft key is cleared on success.

Bug note
Page: http://127.0.0.1:3000/invoices/1042
Account: Avery (seed)
Browser: lab profile
Preserve log: on

Click: Save note, text "checked April retainer"
Row: POST /api/notes
Status column: (canceled)
Response pane: empty
Initiator: /static/app.js click handler
Next row: GET /invoices 200, same initiator

API after reload: text absent
local storage harbor.draftNote: may still hold the text
Change: navigate only after the POST completes, then clear the draft key

Check yourself

1. Which capture shows that Save note aborted its own request?

2. The API does not contain the sentence, and local storage still does. What should the note say?

3. After you change the click handler to wait for the POST, which result means the bug is gone?

4. Where does this walkthrough send traffic?