Browser debugging
The Difference Between Debugging The Request And Debugging The Feature
Published June 25, 2026
I see agents get stuck on frontend bugs when they treat every failure as a feature bug. The button does not work, so they edit the button. The form does not save, so they edit the form.
Most of the time I want the agent to answer a smaller question first. Did the browser send the request I expected, and did the server answer with the shape the feature needs?
The mental model
Debugging the request means following one browser action across the network boundary. You inspect the URL, method, headers, payload, status code, redirect chain, timing, response body, and console errors attached to that request.
Debugging the feature means checking whether the product behavior is correct. The user clicked Save. The modal should close, the list should include the new item, and the error message should be specific. The page should not lose unsaved input.
I treat those as different jobs. A request can be correct while the feature is wrong. A feature can look broken because the request is wrong. Mixing the two makes agents patch the closest visible code instead of the code that caused the failure.
Start with the request
I start with the request when the feature crosses a boundary: login, checkout, file upload, search, invite sending, license activation, webhooks, analytics, or anything backed by an API call.
Chrome DevTools documents the Network panel as the place to check whether resources are uploaded or downloaded and to inspect headers, content, size, and related request details. That is the first split I want an agent to make.
Click Save
Find the request
Check method, URL, status, payload, response
Then inspect UI stateIf there is no request, the bug is probably before the network boundary. I look at the click handler, disabled state, validation branch, form submit event, route guard, or feature flag.
If the request exists but the payload is wrong, I look at state mapping. The bug is usually in the form model, serializer, default values, or a stale closure that captured old state.
If the payload is right and the server returns a 4xx or 5xx, I stop editing the UI. MDN groups status codes into informational, successful, redirection, client error, and server error classes. A 400-class response and a 500-class response point to different work.
A concrete example
Say an agent is fixing a license activation screen. The user pastes a key, clicks Activate, and the page stays on the same screen.
The feature-level report is too vague:
Activation does not work.The request-level report is useful:
POST /activate
payload = licenseKey redacted
status = 200
response = {"ok":true,"expiresAt":"2027-06-25T00:00:00.000Z"}
console = TypeError reading result.license.activatedNow the next edit is obvious. The request succeeded. The feature failed after the response came back because the UI expected a different response shape.
If the request-level report had said status = 401, I would check auth. If it said status = 0 or showed a CORS failure in the console, I would check origin and server headers. If it never showed a request, I would check the click path.
Then debug the feature
Once the request is known good, I switch back to product behavior. Browser inspection matters more than API inspection at that point.
For the same activation screen, I want checks like these:
- The success state is visible after the response.
- The activation button is not stuck in loading state.
- The error banner is cleared after a successful retry.
- The license state survives reload.
- The page does not show both success and failure UI at the same time.
None of those are answered by a 200 response. They require DOM state, visible text, storage, routing, and sometimes a screenshot. Chrome's Console docs also matter here because the browser can log network errors, uncaught exceptions, and slow handler warnings that explain why the UI did not settle.
The agent workflow I use
For browser features, I want the agent to collect request evidence and feature evidence separately.
1. Capture the starting DOM state.
2. Perform the user action.
3. Read the network request and response.
4. Read console errors.
5. Read the DOM diff.
6. Decide whether to edit request code or feature code. With DevSnoop, that maps naturally to page_summary, click, get_network, get_logs, and diff. The product name matters less than the evidence. I want the agent to see both sides of the boundary before editing code.
A screenshot-only loop is weaker here. It can show that the page still looks wrong, but it cannot tell whether the POST was missing, malformed, rejected, redirected, cached, or followed by a JavaScript exception.
What to ask before editing
These are the questions I want answered before an agent opens the editor.
- Did the expected request happen?
- Was the method right?
- Was the URL right, including environment and route prefix?
- Was the payload the current UI state or old state?
- Did the browser block the request before the server saw it?
- Did the server return success, redirect, client error, or server error?
- Did the response shape match what the UI reads?
- Did the DOM change after the response?
- Did the console log an exception after the response?
That list catches a lot of bad edits. A missing request sends you to event wiring. A bad payload sends you to state serialization. A good response with a broken DOM sends you to UI state. A console exception sends you to the line that crashed.
Where agents usually go wrong
The common failure is editing the visible component before checking the hidden boundary. The button is visible, so the agent changes the button. The error is visible, so the agent changes the error component. The route is visible, so the agent changes the route.
The edit sometimes works. It also produces fixes that pass one screenshot and fail the real path.
I prefer a small rule: if a browser action should cause a request, inspect the request before editing the feature. After the request is understood, inspect the feature like a user would.