Skip to content

pm view: match the requested range against the buffer it was parsed from - #38671

Open
robobun wants to merge 1 commit into
mainfrom
farm/b5787e8d/pm-view-range-buffer
Open

robobun wants to merge 1 commit into
mainfrom
farm/b5787e8d/pm-view-range-buffer

Conversation

@robobun

@robobun robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • bun info pkg@<range> and bun pm view pkg@<range> resolve the wrong version, or none, when a comparator in the range carries a prerelease tag longer than 8 bytes. Against a registry whose only versions are 1.0.0-beta.20240101 and 1.0.0-beta.20240301 (latest = .20240301), bun 1.4.0 gives:
    • pkg@'>=1.0.0-beta.20240101': error: No version of "pkg" satisfying ">=1.0.0-beta.20240101" found (should resolve to .20240301)
    • pkg@'~1.0.0-beta.20240301': same error (should resolve to .20240301)
    • pkg@'<1.0.0-beta.20240201': resolves to .20240301 (should be .20240101)
    • pkg@'<1.0.0-beta.20240101': resolves to .20240301 (should match nothing)
  • Cause: src/runtime/cli/pm_view_command.rs:213 parses the range out of the command line argument (SlicedString::init(version, version)) but then calls find_best_version(&query, &parsed_manifest.string_buf), so the range's prerelease tags are read out of the manifest's string buffer. The comment on that line said it mirrors outdated_command.rs, but there the range comes from the lockfile and the lockfile buffer is passed, which is the matching buffer for that caller.
  • Tags of 8 bytes or less are stored inline and are unaffected, and an exact version (pkg@1.0.0-beta.20240101) is unaffected because find_best_version takes the exact-version fast path, which looks the version up by tag hash. Only ranges hit the bad path.
  • The Zig version of this command had the same call, so this is not a port regression.

Fix

  • Pass version (the argument slice the query was parsed from) as the group buffer, the same contract every other find_best_version / find_best_version_with_filter caller follows (outdated_command.rs, update_interactive_command.rs, PackageManagerEnqueue.rs all pass the lockfile buffer their range was parsed from).
  • Correct because find_best_version(group, group_buf) slices the group's tags out of group_buf (Group::satisfies(v, group_buf, &self.string_buf) and left.version.order(latest, group_buf, &self.string_buf) in src/install/npm.rs), and query::parse(version, SlicedString::init(version, version)) stores those tags as offsets into version.
  • Test: test/cli/install/bun-info.test.ts, describe("version ranges with prerelease tags longer than 8 bytes"). A Bun.serve mock registry serves the two-version packument above; for both bun info and bun pm view it checks the two cases resolved through the latest dist-tag, the case that walks the prerelease list, and the case that must match nothing. All 8 fail on the released binary with the outputs listed under Problem and pass with this change; the whole file (28 tests) passes with the debug build.
    • The package name in the test is longer than the ranges so that, without the fix, the bytes read from the manifest buffer are part of the package name and never happen to equal the tag (with a very short name the manifest buffer would line up with the argument by accident and the test would pass either way).

Background

  • Semver values (Version, its prerelease/build Tag, range Groups) do not own their text. A string of up to 8 bytes is stored inline in the 8-byte semver::String; anything longer is stored as an offset and length into whichever buffer it was parsed from, and every slice/order/fmt on it has to be handed that same buffer. Passing a different buffer is bounds-checked, so it yields unrelated bytes (or an empty string) rather than a crash, which is why this shows up as a wrong answer instead of a failure.
  • bun pm view has two such buffers in play: the command line argument the range was parsed from, and the manifest's string_buf, which holds everything parsed out of the registry response (it starts with the package name, followed by the version strings that have tags). find_best_version takes the range's buffer as a parameter and uses its own string_buf for the candidate versions.
  • Comparing a prerelease version against a prerelease comparator (Tag::order_pre) compares the two tags dot-segment by dot-segment; the major/minor/patch and "has a prerelease at all" comparisons happen before that and do not touch the buffers. So the wrong buffer only matters when both the comparator and the candidate have a prerelease tag at the same major.minor.patch, which is exactly the case the test exercises.

`bun info pkg@<range>` / `bun pm view pkg@<range>` parse the range out of
the command line argument, so prerelease tags longer than the 8 byte
inline limit are stored as offsets into that argument. find_best_version
was handed the manifest's string buffer for the range, so those tags were
read out of the wrong buffer and ranges like `>=1.0.0-beta.20240101`
matched the wrong versions or nothing at all. Exact versions were not
affected because they are looked up by tag hash.
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ec6e1c6-7b0e-4d17-8f6a-79d160865016

📥 Commits

Reviewing files that changed from the base of the PR and between 9cff2a1 and 941f975.

📒 Files selected for processing (2)
  • src/runtime/cli/pm_view_command.rs
  • test/cli/install/bun-info.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Status: fix is up for review.

  • Reproduced on the released bun 1.4.0 against a Bun.serve mock registry publishing only 1.0.0-beta.20240101 and 1.0.0-beta.20240301: bun info pkg@'>=1.0.0-beta.20240101' and pkg@'~1.0.0-beta.20240301' report no matching version, pkg@'<1.0.0-beta.20240201' and pkg@'<1.0.0-beta.20240101' resolve to .20240301. bun pm view behaves the same way.
  • Test: test/cli/install/bun-info.test.ts, describe("version ranges with prerelease tags longer than 8 bytes"); 8 tests fail on the released binary and pass with this change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant