Repository navigation
Conversation
…byte The SIMD producer pushed the two end sentinels only on the refill after the one that consumed the last byte. A read of the index entry behind the root value then made a second cold fill_to call and a window move. The scalar producer already pushes the sentinels when it reaches the end. Capacity for both is reserved in with_producer.
Parser::parse_document is the only way to get a root value or the tape out of the stage-2 parser. It parses the root value and then requires end of input. Whitespace, a BOM and comments can follow the value. Other content is reported as 'Expected end of file but found "<token>"'. The check_len parameter chain, parse_utf8_impl, ParsedJson::parse_jsonc_document and Parser::unexpected_here are deleted. Their callers (.npmrc values, pm pkg set --json, bun lint) call the plain entry points, which now have the same rule. --define values go through the same parser. A value that starts like JSON must be JSON to its end. true, false, null and undefined are keywords only as the whole value: with other content behind the word the value is an auto-quoted string. NODE_ENV is written as an escaped string literal, so a quote or a backslash in it no longer ends the define value early. bun init stops with the parse error when the package.json in the directory does not parse. It replaced the file with the template before.
Collaborator
Author
|
Updated 10:54 PM PT - Oct 15th, 2026
❌ @robobun, your commit eccfa42 has 1 failures in 🧪 To try this PR locally: bunx bun-pr 45386That installs a local version of the PR into your bun-45386 --bun |
Collaborator
Author
|
Status Reproduced on the released 1.4.3 ( # 1. The root object closes early, so "dependencies" is content after the root.
# 1.3.14, 1.4.3 and main: "No packages! Deleted empty lockfile", exit 0.
mkdir dep && echo '{ "name": "dep", "version": "1.0.0" }' > dep/package.json
echo '{ "name": "app", "dependencies": { "dep": "file:./dep" } }' > package.json && bun install
printf '{\n "name": "app",\n "scripts": { "s": "echo hi" }\n},\n "dependencies": { "dep": "file:./dep" }\n}\n' > package.json
bun install
# 2. Git conflict markers after the root object. 1.3.14 refuses this file.
printf '%s\n' '{"name":"first","scripts":{"s":"echo FIRST_BLOCK_RAN"}}' '<<<<<<< HEAD' '{"name":"second","scripts":{"s":"echo SECOND"}}' '>>>>>>> feature' > package.json
bun run s # 1.4.3: prints FIRST_BLOCK_RAN, exit 0
bun pm pkg set foo=bar # 1.4.3: rewrites the file without the markers and the second object |
A --define keyword followed by other content (true false) gives the keyword again. Whether that value is an error or a string is a separate question. A --define value that starts like JSON still has to be JSON to its end, because it goes through the parser. NODE_ENV is quoted as on main. #41476 escapes that value.
The comment above the parse call in src/ini/lib.rs described two Expr types that are one type now. The two-line comment in its error arm and the doc comment of Parser::parse_document are one line each.
robobun
force-pushed
the
robobun/386a5208/json-reject-trailing-content
branch
from
October 16, 2026 03:35
43561a0 to
eccfa42
Compare
This was referenced Oct 18, 2026
This branch has not been deployed
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.
Problem
package.jsoncloses early (},in the middle),bun installdeletesbun.lockand exits 0 on every release.bun run sruns the first object's script. 1.3.14 and npm refuse that file.if check_len && !parser.at_trailing_end()inrun_stage2(src/parsers/json.rs:194). Most callers passfalse.Fix
Parser::parse_documentis the only exit of the parser. It requires end of input after the root value (whitespace and comments pass). Thecheck_lenflag is deleted.bun initstops on apackage.jsonthat does not parse.test/js/bun/jsonc/plus two files. 69 tests fail on main.Background
.jsonimports, registry manifests,Bun.JSONC.parse,--define.falseagain.Downsides
}after the root,require("dep")loadsindex.jsandbun checkskips thecheckscript.},or a second object after the root. 0 of 7,203 scanned JSON files change.Notes
The two commits
json_index.rs), which indexes the structural bytes in 8 KiB windows. It writes the end markers with the last window, so the end-of-input check reads an entry that exists.json_stage2.rs) walks the index.The decision
The rule is: a document is one value, then only whitespace and comments. A yes covers these cases too:
},or}}, as the two fixtures of Support comments & trailing commas in require/importpackage.json#10531 do (test/cli/run/require-and-import-trailing.test.ts). This PR treats that comma as a stray character.--definevalue that starts like JSON and has more content (7 days,5 * 1000).Order
bun runreport a package.json that does not parse, where it now uses a parent's scripts.parse_utf8_impl::<true>andParsedJson::parse_jsonc_document, which this PR deletes, and it changes thepm pkg set --jsonlines that this PR touches. This PR is rebased after it.NODE_ENV. With this PR alone,NODE_ENV='a"b' bun buildis an error, because that value reaches the parser as"a"b".bun initguard here.Readers that hide a parse error (run on 1.3.14, 1.4.3, main
a407eae88aand this PR)}at the end,bun run buildin the membermainand an extra},require("dep")mainindex.jspathsthat ends in},,bun main.tsCannot find module '@lib/x', no word about tsconfig.jsoncheckscript and an extra},bun checkNo type errors, exit 0},bun pm pack --dry-run(1.4.3 and main)Each of these readers already turns any parse error into a default. This PR sends more files down that path. The plan after a yes: #45113 covers the first row. The others fail with the located error in this PR (
has_scriptinscript_or_command.rs,pack_command.rs, and the tsconfig report of #41683).bun pm pkg,bun pm versionandbun addprint the located error, where they print a bareParserErrornow. What the resolver does with a dependency's manifest follows the decision above.Repro
},, thenbun installbun run sbun pm pkg get name"first", exit 0bun pm pkg set foo=barBun.JSONC.parse('{"a":1} {"b":2}'){a:1}{a:1}bun buildof a.jsonwith{"x":1} trailing junk{x:1}{x:1}On 1.4.3,
bun pm pkg delete,bun pm versionandbun addalso rewrite the conflict file without its tail.Tails refused
{"a":1}: 1.3.14 refuses 17, 1.4.3 refuses 1, this PR refuses 94.package.jsonobject throughbun pm pkg get: 1.3.14 refuses 4, 1.4.3 refuses 1, this PR refuses 10. The other 2 are comments, which stay valid.Who sees a new error
bun install,bun remove: print the error with file, line and column, exit 1.bun add,bun pm pkg set,bun pm pkg delete,bun pm version: printParserError, exit 1, leave the file as it is.bun init: prints the error, exits 1, writes nothing (new in this PR). Before, it replaced a package.json that did not parse with the template.require()/importof a package.json or.jsoncfile,bun buildof.json/.jsonc, tsconfig.json inbun build: a build error with file, line and column.--frozen-lockfilefails. Without it,bun installprints the error, ignores the lockfile and writes a new one.JSON.parse, which refuses a tail too.Bun.JSONC.parse: throwsSyntaxError, as its types say (@throws {SyntaxError} If the input is not valid JSONC).--definevalues (39 probed, 1.4.3 against this PR)7 days,1 2,5 * 1000,{"a":1} junk,"a" + "b","a" "b",[1],,[1] [2],-1 x. 1.3.14 refused"a" + "b"too.true falsestill givestrue: the keywords are read before the parser, and that is a separate question.Deleted with the flag
parse_utf8_impl::<CHECK_LEN>:.npmrcvalues andbun pm pkg set --jsoncallparse_utf8.ParsedJson::parse_jsonc_document(Addbun lintandbun format#44805):bun lintcallsparse_jsonc, which has the same rule now. The unit test that compared the two keeps its rows and loses the three assertions that the plain entry accepts a tail.Parser::unexpected_here.parse_value,at_trailing_end,take_tapeandcursorare private to the parser.Tests that pinned the old behaviour
json-test-suite.test.ts: 11 JSONTestSuiten_*rows with content after the value move from "valid JSONC" to their own list that asserts the error.jsonc.test.ts: the window-seam test for{"k":1}@@@@asserts the error.require-and-import-trailing.test.ts: the two fixtures end in},. Their input stays, and they now assert the error. Two new tests with the same file ending in}assert what Support comments & trailing commas in require/importpackage.json#10531 added (comments and a trailing comma inside the object).bun-pm-pkg.test.ts: two--jsonrows get the new message (Unexpected 2is nowExpected end of file but found "2").Measurements (release builds of main
a407eae88aand of this PR atcb1bb706c, Linux x64. The later commits put the--definekeyword arm and the call form of one cold helper back as on main. Not measured again.)Instructions of one whole
parse_impl_incall, counted with gdbstepion the thirdBun.JSONC.parsecall of each input:StructuralIndex::fill_tocalls per document: 1 on both builds (2 for the 20 KB file on both). The first commit is why: without it the check would ask for an index entry that the last refill had not written yet.bunis 106,178,312 bytes on both builds.sizereports text 106,007,471 against 106,006,447 (+1,024). Thebun_parsers::json*symbols sum to 54,754 bytes against 54,389 (+365,nm -S).check_lenlines injson.rs: 0 (main 12). Crate-visible items injson_stage2.rs: 4 (main 8).scripts/bench-json-rust.shdoes not build on main (it compileshighway_json.cppas C++17, which now needs C++20).Designs weighed
--define(commit d7272bf): that door keeps returning the first value (5 * 1000is5).}},},or a second object still pass, and no other tool has that rule.}.Self-review
NODE_ENVquoting copied bundler: JSON-escape the NODE_ENV auto-define value #41476, and the--definekeyword rule is its own question. Addressed: both are out (4535dd2).Not in this PR
Bun.JSONL.parsedrops content after a value on a line (Bun.JSONL: reject extra content on a line instead of silently dropping it #36029).bun initalso replaces a package.json whose root is not an object. init: never write over a package.json that was not loaded #41807 reworks that whole path.--definevalue prints a second, internal error line after the parse error.{}at every entry point.Tests
test/js/bun/jsonc/jsonc.test.ts,test/js/bun/jsonc/json-test-suite.test.ts,test/cli/run/require-and-import-trailing.test.ts,test/cli/install/bun-pm-pkg.test.ts.cargo test -p bun_parsers --lib --release: 41 passed, 0 failed. CI type-checks these tests but does not run them.test/regression/issue/32686.test.ts,test/js/bun/resolve/jsonc.test.ts,test/bundler/bundler_loader.test.ts,test/js/bun/ini/ini.test.ts,test/bundler/bundler_env.test.ts.--definetests are injsonc.test.tsbecause they test the same parser..jsonand.jsoncfiles (4,140 package.json) innode_modules, the install cache, Node's test fixtures and this repo. 1.4.3 and this PR accept the same 7,187.Credit: this starts from #35072 and from commit d7272bf (
robobun/b262bd0b/json-reject-trailing-content), whose test matrix it keeps.no test proof · iteration 2 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/install/bun-pm-pkg.test.ts