fix(tail): fall back to raw line on malformed JSON instead of aborting - #312
Open
detail-app[bot] wants to merge 1 commit into
Open
detail-app[bot] wants to merge 1 commit into
detail-app[bot] wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Malformed JSON is now handled as raw line data in the explicitly enabled JSON-policy tail path instead of terminating the stream, while out-of-memory errors remain fatal. The implementation is isolated to parsing with focused regression coverage; the second changed file contains tests only. You can add or adjust custom eligibility rules. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Detail bug report: View on Detail
Fixes ENG-713
Bug
When
edge-tailruns with both-f jsonand--policy, a single syntactically invalid JSON line aborts the entire run with a non-zero exit (error.SyntaxError/error.UnexpectedEndOfInput). The malformed line and all lines after it are dropped; the run stops at the bad line.Root cause:
parseJsonAttrsinsrc/tail/eval_parse.zigusedtry std.json.parseFromSliceLeaky(...), so a malformed line's parse error propagated out ofparseLine→evalLineResult→evalLine→ the framer →runStream/runFilesLoop→main, killing the process. This was inconsistent with the existing handling of valid-but-non-object JSON values, which already fell back to the raw line viaif (parsed != .object) return;.This only triggers when an operator opts into both
-f jsonand--policy(the shipped defaults are.raw/ no policy, where parsing is never reached).Fix
Scope error handling in
parseJsonAttrsso per-line content/structure errors fall back to the raw line whileerror.OutOfMemorystill propagates:This makes a malformed JSON line class with the already-non-fatal valid-but-non-object case (and the framer's oversized-line fail-open behavior): the line is kept verbatim as its own
message/body and the run continues.error.OutOfMemorystays fatal-to-parseLine(resource exhaustion is a machinery failure, not a per-line content problem), as does OOM from the post-parsedupe/appendcalls which still use baretry. A blanketcatch {}was avoided so genuine memory pressure is never silently swallowed.The fix is intentionally in
parseJsonAttrs, notparseLine—parseLine's existingif (ctx.message == null) ctx.message = line;fallback then becomes reachable on the error path.Testing
task lint=zig fmt --check+ziglint), and build all pass.zig build testis green (522 pass, 1 pre-existing skip, 0 fail).eval parse: malformed json falls back to raw line— covers theSyntaxErrorpath.eval parse: truncated json falls back to raw line— coversUnexpectedEndOfInput(a realistic trigger: producer killed mid-write, log rotation mid-line), and guards against narrowing the catch to onlySyntaxError.eval stream public API: malformed json line does not abort evaluation— covers the integration path: a keep/drop policy still applies to the malformed line's raw body, and a subsequent valid JSON line is still parsed and evaluated correctly.eval parsetests fail withSyntaxError/UnexpectedEndOfInputand theeval streamtest fail on the malformed line — proving the tests genuinely guard the bug.edge-tailbinary (zig build tail), input{"message":"first"}\n{not valid json}\n{"message":"third"}\nwith a keep-all policy:error: SyntaxErroron stderr, only{"message":"first"}persisted — reproduces the report.-f raw --policy(no JSON parsing) and-f jsonwithout--policy(disabled evaluator) both still exit 0 with all three lines, confirming the bug remains gated on both flags.--read-from head -f json --policy): the fixed follower stays alive and emits all three lines; the buggy follower self-exits on the malformed line.std.testing.FailingAllocator:error.OutOfMemorypropagates from both the parse call (theerror.OutOfMemory => return errarm) and the post-parsedupe/append(baretry), so the scoped catch does not silently drop attribute enrichment under memory pressure.bench/logging) was run whereuvwas installed during testing. The-f json --policyhappy-path regression guard (test_policy_json_attribute_drop) and all other stdin-based tail tests pass. A few file-tail/chmod-based tests fail or hang in this sandbox, but those are pre-existing/environmental (confirmed by re-running them on the baseline with the fix stashed — they fail identically; thechmod(0)failure-injection tests hang because the environment runs as root, which bypasseschmod(0)) and none of them exercise the-f json --policypath this fix touches.Automatic Fixes PRs can be configured here.