Skip to content

feat: make support error-report emails legible, add a version 2 report, and fix #835 items 1-2 - #2014

Merged
jcschaff merged 6 commits into
masterfrom
feat/legible-error-report-emails
Aug 21, 2026
Merged

feat: make support error-report emails legible, add a version 2 report, and fix #835 items 1-2#2014
jcschaff merged 6 commits into
masterfrom
feat/legible-error-report-emails

Conversation

@jcschaff

@jcschaff jcschaff commented Aug 21, 2026

Copy link
Copy Markdown
Member

Closes #1495. Addresses items 1 and 2 of #835.

The problem

An error report reaches vcell_support@uchc.edu as the raw JSON it was posted in: one line, every newline a literal \n, every apostrophe a '.

Five real messages, read-only from the support mailbox: 112 KB, 136 KB, 138 KB, 188 KB, 434 KB — each a single line.

Most of that is duplication. DialogUtils built the report as new RuntimeException(supplemental, exception), so the client log became the wrapper's message and landed twice — as exceptionMessage and again at the head of stackTrace. On one 124 KB report:

chars
body 123,975
client log, carried twice 116,558 — 94%
actual exception chain + frames 7,417

Frames began at line 219, past 58,000 characters. And because the report was wrapped, the stack trace was the wrapper's — rooted at DialogUtils:766, with the real fault demoted to a Caused by:.

Changes

1. Render the email as text (server side). Header (user, version, platform) plus headed sections. For reports from existing clients it also removes the duplicate log, leaving a marker where the stack trace had inlined it.

2. A version 2 report (client side). Each part gets its own field — modelInfo, clientLog, userEvents — and the exception is sent as thrown. A reportVersion field marks the form; the server renders both, so neither side deploys in lockstep.

3. #835 item 1 — users can say what they were doing. An optional one-line field in the error dialog. A log records what VCell did; only the user can say what they were trying to do. Empty sends exactly as before.

4. #835 item 2 — reports identify the reporter. ErrorUtils was told who was logged in once, at startup, from the command-line user name — normally absent, since people log in through the dialog. Every report was therefore anonymous. It is now told again on each successful connect.

5. The error dialog always has an owner. LWNamespace.findLWOwner returns null when no ancestor implements LWContainerHandle (it logs an error when that happens, so it does). AWT downgrades DOCUMENT_MODAL to modeless when the owner is null, so the error dialog would neither block the window that raised it nor stay in front of it — one way an error ends up behind the main window. findLWOwnerOrMostRecent falls back to the most recently focused visible top-level window.

Result on the real report

before after
body 123,975 chars 66,196
lines 1 1,106
first frame past 58,000 chars first screen

The dialog, rendered from the built classes:

Cannot invoke "cbit.vcell.mapping.ElectricalTopology.getGroundElectrode()"

What were you doing? (optional)  [________________________]

Send error report to VCell development team to assist debugging?   [?]

Tests

ErrorUtilsTest 3 → 14. The 3 existing JSON round-trip cases pass unchanged — the check that the wire format still serialises as before. New cases cover field rendering, the body not being one long line, CRLF normalisation, missing fields, a multi-line platform staying on its header line, the legacy log appearing exactly once with frames ahead of it, a short exception message left alone, and the v2 form.

Not addressed

  • Error Reports from Contact-Us improvements #835 item 3 (include database transactions).
  • The subject line stays "VCell Support sent through Contact-Us". Including the exception type would make the inbox scannable, but these are forwarded by a mail rule that may match the exact subject — worth checking the rule first.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn

jcschaff and others added 4 commits August 21, 2026 11:51
Support reports arrive at vcell_support as the raw JSON they were posted
in: one line, tens of thousands of characters, every newline written as a
literal backslash-n and every apostrophe as a unicode escape. Measured on
five real reports, the bodies run from 112 KB to 434 KB, each a single
line.

ErrorReport.toEmailText() renders the same content as plain text with a
header (user, version, platform) and headed sections.

It also drops a duplicate. Reports are raised as new RuntimeException(log),
so the client log arrives twice: once as exceptionMessage, and again as the
message of the exception that opens the stack trace. On a real report that
is 94% of the body, and the second copy buries the exception chain behind
58 KB of routine logging. The log is now carried once, at the end, and the
stack trace keeps a marker where it was.

On that report: 124 KB to 66 KB, one line to 1106, and the frame naming
the actual fault is on the first screen instead of past 58,000 characters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn
The wire format between client and server is unchanged -- the report is
still posted as JSON -- so this improves the email from every client
already installed, with no client update needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn
Version 1 arrives as one blob: the caller wrapped the client log, the
model info and the recorded user events in the message of a new
RuntimeException before sending, so the log landed in exceptionMessage
and again at the head of stackTrace, and the frames identifying the fault
sat past fifty thousand characters of routine logging.

Version 2 gives each part its own field -- modelInfo, clientLog,
userEvents -- and sends the exception as it was thrown. A reportVersion
field says which form it is; the server renders both, so a client of
either vintage is understood and nothing has to be deployed in lockstep.

The legacy renderer keeps the de-duplication heuristic for reports from
older clients. Version 2 needs none of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn
… exception

DialogUtils built the report as new RuntimeException(supplemental,
exception), which had two costs: the log was carried twice, and the stack
trace was the wrapper's -- rooted at DialogUtils rather than at the fault.

It now sends the exception as thrown, with the model, the log and the
recorded events as separate fields. DialogMessagePanel gained accessors
for the model and the log so they no longer have to be concatenated and
pulled apart again.

Reporting an error can no longer raise one: the send is guarded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn
@jcschaff jcschaff changed the title feat: make VCell support error-report emails legible feat: make support error-report emails legible, and add a version 2 report Aug 21, 2026
jcschaff and others added 2 commits August 21, 2026 12:27
…ported it

Addresses the first two items of #835.

The error dialog gains an optional one-line field, "What were you doing?".
A log records what VCell did; only the user can say what they were trying
to do, which is often what makes a report reproducible. An empty field
sends exactly as before.

Reports were also arriving anonymous. ErrorUtils was told who was logged
in once, at startup, from the command-line user name -- normally absent,
because people log in through the dialog. It is now told again on each
successful connect, so a report carries the account and support can follow
one up.

Item 3 of #835 (include database transactions) is not addressed here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn
LWNamespace.findLWOwner walks up looking for an LWContainerHandle and
returns null when it finds none -- it logs an error when that happens, so
it does happen. The dialog was then constructed with a null owner, and AWT
downgrades DOCUMENT_MODAL to modeless when there is no owner: the error
dialog would neither block the window that raised it nor stay in front of
it, which is how an error message ends up behind the main window.

findLWOwnerOrMostRecent falls back to the most recently focused visible
top-level window, which liveWindows() already returns first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUd61NUJgz88h4PZs5MqHn
@jcschaff
jcschaff force-pushed the feat/legible-error-report-emails branch from 7164af7 to a96f2da Compare August 21, 2026 16:28
@jcschaff jcschaff changed the title feat: make support error-report emails legible, and add a version 2 report feat: make support error-report emails legible, add a version 2 report, and fix #835 items 1-2 Aug 21, 2026
@jcschaff
jcschaff merged commit 3e6bf7c into master Aug 21, 2026
9 checks passed
@jcschaff
jcschaff deleted the feat/legible-error-report-emails branch August 21, 2026 16:37
@jcschaff jcschaff mentioned this pull request Aug 21, 2026
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.

VCell Support Automated Email message are too opaque to be very useful

1 participant