Skip to content

Capture exception message in browser telemetry#304

Open
archandatta wants to merge 3 commits into
mainfrom
hypeship/telemetry-exception-message
Open

Capture exception message in browser telemetry#304
archandatta wants to merge 3 commits into
mainfrom
hypeship/telemetry-exception-message

Conversation

@archandatta

@archandatta archandatta commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Runtime.exceptionThrown telemetry events set console_error.data.text to CDP's generic "Uncaught" / "Uncaught (in promise)" prefix and dropped the actual error message. The message lives on the exception RemoteObject (exceptionDetails.exception), which was already parsed into the struct but never read — so consumers could see that an exception fired and where (line/column/source_url/stack), but not what it said. console.error() messages were unaffected.

Changes

  • Add message to BrowserConsoleErrorEventData (openapi.yaml + regenerated oapi).
  • Decode a minimal RemoteObject (description, value) and derive the message: first line of description (e.g. Error: boom) → thrown value for non-Error throws → fall back to the generic text. The full stack stays in stack_trace, so message is just the name+message line.
  • message is present only on exceptionThrown-sourced events; text is left unchanged so the uncaught-vs-in-promise signal is preserved.
  • Unit tests for the derivation (Error, primitive throws, fallbacks).

Test plan

  • go build ./...
  • go test ./lib/cdpmonitor/ -run TestExceptionMessage
  • e2e: assert an injected throw new Error("...") yields console_error.data.message containing the message (needs Docker + image)
  • Verify the field propagates through the public API / SDK telemetry path (kernel/kernel) — additive/back-compat; add there if it re-serializes through a typed schema

Note

Low Risk
Additive optional API field and localized CDP handler logic with unit tests; no auth or breaking changes to existing text behavior.

Overview
Runtime.exceptionThrown console error events now expose the real exception text via a new optional message field on BrowserConsoleErrorEventData, while text stays as CDP’s generic "Uncaught" / "Uncaught (in promise)" prefix.

The CDP monitor unmarshals the exception RemoteObject and derives message with exceptionMessage: first line of description for Error throws (stack remains in stack_trace), thrown primitives via value, unserializableValue for Symbol/BigInt, then fallback to text. OpenAPI and generated oapi types were updated; consoleAPICalled paths are unchanged.

Reviewed by Cursor Bugbot for commit 1c55b27. Bugbot is set up for automated code reviews on this repo. Configure here.

Runtime.exceptionThrown events set console_error.data.text to CDP's
generic "Uncaught" prefix and dropped the actual error message, which
lives on the exception RemoteObject (already parsed but unused). Add a
`message` field populated from the object's description (first line) or
thrown value, so consumers can see what an exception said, not just that
one occurred and where.
@archandatta archandatta marked this pull request as ready for review July 3, 2026 18:11
Comment thread server/lib/cdpmonitor/handlers.go
Comment thread server/lib/cdpmonitor/handlers.go
A thrown JSON null rendered as "<nil>" instead of falling back to the generic
text; guard the nil case. Symbol/BigInt/NaN/Infinity throws land in
unserializableValue, which was ignored and dropped the thrown value; read it
after description. Adds regression tests for both.
if i := strings.IndexByte(o.Description, '\n'); i >= 0 {
return o.Description[:i]
}
return o.Description

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leading newline yields empty message

Medium Severity

The exceptionMessage function returns an empty string when a CDP exception's description begins with a newline. This occurs because the first-line extraction logic misinterprets a leading newline, resulting in a blank message field in telemetry despite the generic text fallback.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 77185e4. Configure here.

@archandatta archandatta requested review from Sayan- and rgarcia July 3, 2026 18:46

@rgarcia rgarcia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed the openapi.yaml change — one schema suggestion:

  • server/openapi.yaml:1771 — having both text and message on BrowserConsoleErrorEventData forces consumers to learn a rule ("prefer message if present, else text"), and the required field (text) is the near-useless one on the exception path ("Uncaught"). suggest dropping message entirely and making text the full console-rendered line on the exception path — that's exactly what DevTools displays (exceptionDetails.text + " " + exception.description first line, e.g. Uncaught Error: boom), so it's zero invention over CDP, keeps one required field, and preserves the uncaught-vs-in-promise signal inside the string. since this is beta, no back-compat concern. suggested description:

    text:
      type: string
      description: >
        Human-readable error text, as the browser console would display it.
        For console.error() calls, the first argument coerced to a string.
        For uncaught exceptions, the prefix and error message, e.g.
        "Uncaught Error: boom" or "Uncaught (in promise) TypeError: x is not a function".

    implementation-wise this is a small change at handlers.go:228-229 — join p.ExceptionDetails.Text with the derived message, taking care the existing fallback-to-text in exceptionMessage doesn't produce "Uncaught Uncaught".

Keep the console_error data field descriptions consistent: state the
contract and the "Present only when sourced from Runtime.exceptionThrown"
qualifier like its siblings, without the derivation detail.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1c55b27. Configure here.

}
return string(o.Value)
}
return fallback

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valueless throws omit type label

Low Severity

exceptionMessage parses a minimal cdpRemoteObject without type, so when CDP sends a valueless primitive (e.g. throw undefined) with no description, value, or unserializableValue, it falls back to generic text instead of the thrown type. consoleArgString already uses Type in the same situation.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1c55b27. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants