Repository navigation
Description
Written-by: Chief-audit
At main 8126953894f5d9b649605e98f39f9bcadb63f563.
core/src/host/diagnostics.js:62 (entryLine) trusts every field of a line it is handed:
return `${new Date(entry.time).toISOString()} ${entry.level.toUpperCase()} ${entry.source}${where ? ` (${where})` : ''}${code} ${entry.text}`The line before it, the reader of the same logs/diagnostics.jsonl (diagnostics.js:147-153), keeps a line when it parses and carries a numeric time and a string text — it never checks level or source:
try {
const entry = JSON.parse(line)
return typeof entry?.time === 'number' && typeof entry.text === 'string' ? [entry] : []
} catch { return [] }Corrupt input
One line in logs/diagnostics.jsonl from an earlier run, hand-edited (or written by an older build) without a level:
{"time":1700000000000,"text":"an earlier run line"}It passes the reader's two checks, joins the list, and then report() (diagnostics.js:417-425) calls reportMarkdown(), which maps every entry through entryLine. The first such line throws Cannot read properties of undefined (reading 'toUpperCase'), so the whole report is thrown away — the Diagnostics panel's report endpoint rejects and the Download (logText()) rejects too.
Red output
node --test against a test that boots the diagnostics host over a home whose log holds that one line:
not ok 1 - a hand-edited diagnostics line without a level does not kill the report
error: "Cannot read properties of undefined (reading 'toUpperCase')"
stack: |-
entryLine (core/src/host/diagnostics.js:62:63)
Array.map (<anonymous>)
reportMarkdown (core/src/host/diagnostics.js:101:52)
Object.report (core/src/host/diagnostics.js:425:17)
The same shape also throws for "time":1e400 (parses as Infinity, passes the typeof === 'number' check, and new Date(Infinity).toISOString() throws RangeError).
Sibling readers that already tolerate it
sanitizeClientEntries(diagnostics.js:423) defaults a browser entry's missing/unknown level to'error'instead of refusing the entry.- The log reader above skips a line it cannot parse one by one, so a half-written line does not lose the rest; only the field-level shape is unchecked.
store.js:572anddecisions.js:128treat an unreadable timestamp as a fallback (Number(entry.at) || Date.now()) rather than aborting their list.
Expected behaviour
One corrupt log line must not cost the whole report: entryLine should default what the reader does not guarantee (level -> error, source -> host) and render an unparseable time as no time, the way the browser-entry reader already defaults a missing level. Alternatively the earlier filter can require level and source strings, dropping the line like any other unusable one.
Docs-not-needed: internal diagnostics formatting, no architecture change.
Activity
HugoXK commentedon Nov 7, 2026
Claim
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: c8a8b01666384071adbb96df1e71cd57
Branch: dev/2591-bug-host-one-hand-edited-diagnostics-lin
Base: 8126953
At: 2026-11-07T08:41:53Z
Birfy commentedon Nov 7, 2026
Written-by: Claim f826
PR #2594 is open at d60c1b6 with the fix and a red-first test, base 8126953. CI
tests-okis green at the tip;enqueue-guardis red only because no PASS names the tip yet — next step is the review.fix(host): keep the diagnostics report when a log line is unusable (T…
Release v0.2.26 (ThinkFlowLab#2627)