Skip to content

parser: truncated blocks are emitted, -1 line numbers navigate to the wrong file, unicode filenames are invisible #5

Description

@GabrielBBaldez

Three parser bugs, all of which end with the user looking at the wrong thing or at nothing. Each is small and independently testable in src/stParser.ts.

1. Truncated blocks are emitted, and the spec says they must not

FORMAT.md is explicit: a block whose closing line is absent is incomplete, and parsers must discard it rather than emit a partial entry.

We do the opposite. The inner loop breaks on the next START — the comment at line 49 literally says "next block began — this one was truncated" — and then line 58 calls parseBlock(block) unconditionally, with no check that the block ended in END.

Why it is visible: the writer emits the header, the session marker and the block as separate writes, and a multi-KB block is not an atomic append. Our watcher fires on that write, so the top row of the list becomes a report with a headline, no culprit (the at ... line has not landed yet), no stack and no story. With no culprit the item gets no command (extension.ts:91-97), so clicking it does nothing, and Copy-for-AI hands the assistant half a report. It heals on the next refresh, which makes it look random rather than reproducible.

Fix: only accept a block whose last line starts with END. Keep the trailing partial in a pending field and re-evaluate it on the next refresh, so a genuinely killed process drops the tail instead of flickering it.

2. A frame with no line number navigates to the wrong file

The frame regex requires \d+:

const FRAME = /\(([\w$]+\.(?:java|kt|groovy|scala)):(\d+)\)/;

But the library writes getLineNumber() verbatim (StackDistiller.java:203), and StackTraceElement.getLineNumber() returns -1 when the class was compiled without -g:lines and -2 for a native method. So a line like at OrderService.confirm(OrderService.java:-1) <- YOUR CODE is legal, routinely produced, and matches nothing.

The marked culprit then yields no frame, culprit stays undefined, and we fall back to frames[0] — which for a wrapped exception is the frame on the wrapped by: line. The user clicks an IllegalStateException attributed to OrderService and silently lands in CheckoutService.java:88.

Fix: allow -?\d+; treat a line number <= 0 as found but not navigable; only fall back to frames[0] when the block contained no marked frame at all.

3. Non-ASCII source file names are invisible

[\w$]+ is ASCII-only in JS without the u flag. Names like Acao.java, Groesse.java or a CJK class name are all legal Java source names — Java identifiers accept Unicode letters — so those frames match nothing and the report ends up with no culprit at all.

Fix: [\p{L}\p{N}_$]+ with the u flag, or simply [^\s:()]+\.\w+. Add .kts to the extension list while you are there.

Verify

A test in src/test/parser.test.ts per case: a truncated block is dropped; a -1 line number is parsed but marked non-navigable and does not steal the culprit; a non-ASCII file name resolves. npm test green.

The same three bugs exist in the JetBrains plugin — the parsers are deliberate twins, so please keep the fixes in step.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingformatThe st/1 / st-json/1 report format (public API)good first issueGood for newcomers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions