Skip to content

fix(docs): correct off-by-one and comment in Range request example#28031

Open
robobun wants to merge 4 commits intomainfrom
claude/fix-range-request-docs-28030
Open

fix(docs): correct off-by-one and comment in Range request example#28031
robobun wants to merge 4 commits intomainfrom
claude/fix-range-request-docs-28030

Conversation

@robobun
Copy link
Collaborator

@robobun robobun commented Mar 12, 2026

Summary

  • Fix off-by-one in Range request example: use end + 1 in Blob.slice() since HTTP Range headers are end-inclusive but slice() is end-exclusive
  • Fix misleading comment: .split("=") on "bytes=0-100" produces ["bytes", "0-100"], not ["Range: bytes", "0-100"]

Closes #28030

Test plan

  • Verified Blob.slice() is end-exclusive: new Blob(["0123456789"]).slice(0, 4).text() returns "0123" (4 bytes)
  • Verified .split("=") output: "bytes=0-100".split("=") returns ["bytes", "0-100"]

🤖 Generated with Claude Code

The HTTP Range header uses inclusive end bounds, but Blob.slice() uses
exclusive end, so `end + 1` is needed. Also fix the split comment to
show the actual output of headers.get("Range").split("=").

Closes #28030

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@robobun
Copy link
Collaborator Author

robobun commented Mar 12, 2026

Updated 12:33 AM PT - Mar 12th, 2026

✅ Your commit d1050ab has passed in Build #39350! 🎉


🧪   To try this PR locally:

bunx bun-pr 28031

That installs a local version of the PR into your bun-28031 executable, so you can run:

bun-28031 --bun

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d0d72493-3482-4116-97ce-04bd514e2da7

📥 Commits

Reviewing files that changed from the base of the PR and between 62a8ea9 and d1050ab.

📒 Files selected for processing (1)
  • docs/runtime/http/routing.mdx

Walkthrough

Update to the documentation example that changes how the HTTP Range header is parsed: the header tokenization now uses the last "=" segment, derives start/end from strings, defaults start to "0", and increments parsed end by 1 for slice semantics.

Changes

Cohort / File(s) Summary
Documentation
docs/runtime/http/routing.mdx
Modify Range header example: parse Range by splitting on "=" and taking the last segment, extract startStr/endStr as strings, default start to "0", and compute end as Number(endStr) + 1 when present to align slice end with Range end.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main changes: fixing an off-by-one error and a misleading comment in the Range request documentation example.
Description check ✅ Passed The description covers both required sections with clear explanations of the fixes and includes a thorough test plan verifying the changes.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #28030: correcting the off-by-one error with end + 1, fixing the misleading .split() comment, and handling open-ended ranges appropriately.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the Range request example in the documentation as specified in issue #28030, with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/runtime/http/routing.mdx`:
- Around line 222-231: The Range parsing currently turns an open-ended end token
into 0 because Number("") === 0; fix the parsing in the Range handling by
extracting the raw end token (e.g. from
req.headers.get("Range").split("=").at(-1).split("-")), set end to Infinity if
the raw end token is empty, otherwise convert it with Number(rawEnd), and only
add 1 when constructing the slice end when an explicit end was supplied; update
the call that returns the file slice (bigFile.slice) to use undefined or omit
the +1 adjustment for open-ended ranges so bigFile.slice(start, end+1) is only
used when end is a real number.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 56f93c13-f1a8-46be-b3f1-3322d850d00b

📥 Commits

Reviewing files that changed from the base of the PR and between a305e99 and 1c23762.

📒 Files selected for processing (1)
  • docs/runtime/http/routing.mdx

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — both the comment fix and the slice off-by-one correction are clearly right.

Extended reasoning...

Overview

This PR modifies a single documentation file (docs/runtime/http/routing.mdx) with two small fixes to a Range request code example: (1) correcting a misleading inline comment about what .split("=") produces, and (2) fixing an off-by-one error by changing bigFile.slice(start, end) to bigFile.slice(start, end + 1) since HTTP Range headers are end-inclusive but Blob.slice() is end-exclusive.

Security risks

None. This is a documentation-only change with no runtime code modifications.

Level of scrutiny

Minimal scrutiny needed — this is a two-line docs fix with straightforward, verifiable correctness. req.headers.get("Range") returns the header value (e.g., "bytes=0-100"), so splitting on = yields ["bytes", "0-100"], not ["Range: bytes", "0-100"]. And Blob.slice() follows the standard end-exclusive convention, so end + 1 is needed to include the byte at the end index.

Other factors

The build passes. No outstanding reviewer comments. The PR description includes a clear test plan verifying both fixes. No bugs were found by the automated bug hunting system.

Avoid Number("") === 0 bug for open-ended ranges like "bytes=100-"
by keeping start/end as strings until after checking for empty end
token. Only applies +1 inclusive-to-exclusive adjustment when an
explicit end was provided; otherwise passes undefined to slice().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Contributor

Found 4 issues this PR may fix:

  1. No Content-Range header when streaming file #17563 - Missing Content-Range header when using Bun.file().slice() for Range requests, exactly the type of implementation that benefits from the corrected documentation example
  2. Incorrect example for bun:sqlite transaction #26044 - Incorrect example in bun:sqlite documentation showing wrong array syntax, similar documentation error pattern of incorrect code examples
  3. stream on sliced Bunfile doesn't work #18192 - Stream freezing with sliced BunFiles, which could be helped by developers understanding proper slice() usage from corrected documentation
  4. Single-line statements can cause off-by-one error in sourcemaps line-number due to not tracking closing brace location #7323 - Off-by-one error in sourcemap line numbers, another instance of the same class of off-by-one bugs being addressed

If this is helpful, consider adding 'Fixes #' to the PR description to auto-close the issue on merge.

🤖 Generated with Claude Code

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/runtime/http/routing.mdx`:
- Around line 222-230: The Range parsing sample in the req.headers.get("Range")
block fails for suffix ranges like "bytes=-500"; update the logic around
startStr, endStr, start and end to special-case when startStr === "" (suffix
range) and compute start as contentLength - Number(endStr) (clamped to >=0) and
set end to contentLength (or undefined for slicing semantics), otherwise
continue treating startStr as the numeric start; alternatively, if you don't
want to support suffix ranges, add a clear comment in the snippet stating it
only supports "bytes=<start>-" or "bytes=<start>-<end>" forms.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b242c70-984c-411f-bc84-5ac409380c35

📥 Commits

Reviewing files that changed from the base of the PR and between 1c23762 and f7e3bcd.

📒 Files selected for processing (1)
  • docs/runtime/http/routing.mdx

Comment on lines +222 to +230
const [startStr = "0", endStr] = req.headers
.get("Range") // "bytes=0-100"
.split("=") // ["bytes", "0-100"]
.at(-1) // "0-100"
.split("-") // ["0", "100"]
.map(Number); // [0, 100]
.split("-"); // ["0", "100"]

const start = Number(startStr);
// Range end is inclusive, but slice() end is exclusive
const end = endStr ? Number(endStr) + 1 : undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does the HTTP Rangeheader support suffix byte ranges likebytes=-500, and what bytes should the server return for that request?

💡 Result:

Yes. HTTP byte-range requests support suffix byte ranges of the form bytes=-N. bytes=-500 means “return the last 500 bytes of the selected representation”. (ietf.org)

If the representation length is L bytes, the server should return the bytes with inclusive offsets:

  • start = max(L − 500, 0)
  • end = L − 1

So:

  • if L ≥ 500: return bytes L-500 through L-1 (the final 500 bytes)
  • if L < 500: return the entire representation (ietf.org)

When the range is satisfiable and the server honors it, the response is typically 206 Partial Content with a Content-Range indicating the selected range. (rfcinfo.com)

Citations:


🏁 Script executed:

# Check the actual content of the file at the specified lines
sed -n '222,230p' docs/runtime/http/routing.mdx

Repository: oven-sh/bun

Length of output: 394


Handle suffix byte ranges per RFC 9110 or narrow the example's scope.

The code doesn't handle suffix byte ranges like bytes=-500 (which should return the last 500 bytes per RFC 9110). When parsing bytes=-500, split("-") yields ["", "500"], so startStr becomes an empty string. Number("") evaluates to 0, resulting in slice(0, 501) and incorrectly returning the first 501 bytes instead of the last 500. Either special-case startStr === "" to handle suffix ranges, or explicitly document that this snippet only supports bytes=<start>- or bytes=<start>-<end> forms.

🛠️ Possible fix
     const [startStr = "0", endStr] = req.headers
       .get("Range") // "bytes=0-100"
       .split("=") // ["bytes", "0-100"]
       .at(-1) // "0-100"
       .split("-"); // ["0", "100"]

+    // `bytes=-500` means "last 500 bytes"
+    const bigFile = Bun.file("./big-video.mp4");
+    if (startStr === "") {
+      return new Response(bigFile.slice(-Number(endStr)));
+    }
+
     const start = Number(startStr);
     // Range end is inclusive, but slice() end is exclusive
     const end = endStr ? Number(endStr) + 1 : undefined;

-    // return a slice of the file
-    const bigFile = Bun.file("./big-video.mp4");
+    // return a slice of the file
     return new Response(bigFile.slice(start, end));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/runtime/http/routing.mdx` around lines 222 - 230, The Range parsing
sample in the req.headers.get("Range") block fails for suffix ranges like
"bytes=-500"; update the logic around startStr, endStr, start and end to
special-case when startStr === "" (suffix range) and compute start as
contentLength - Number(endStr) (clamped to >=0) and set end to contentLength (or
undefined for slicing semantics), otherwise continue treating startStr as the
numeric start; alternatively, if you don't want to support suffix ranges, add a
clear comment in the snippet stating it only supports "bytes=<start>-" or
"bytes=<start>-<end>" forms.

Add comment noting the snippet supports "bytes=N-M" and "bytes=N-"
forms, not suffix ranges like "bytes=-500".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — straightforward docs fix correcting an off-by-one error and a misleading comment in the Range request example.

Extended reasoning...

Overview

This PR modifies a single documentation file (docs/runtime/http/routing.mdx) to fix two issues in a code example showing HTTP Range request handling: (1) correcting an inaccurate inline comment about what .split("=") produces, and (2) fixing an off-by-one error where Blob.slice() needs end + 1 since HTTP Range headers use inclusive end bytes while slice() uses exclusive end. It also properly handles open-ended ranges (e.g., bytes=100-) by checking if endStr is truthy before adding 1, which was addressed after CodeRabbit feedback.

Security risks

None. This is a documentation-only change with no runtime code modifications.

Level of scrutiny

Low scrutiny is appropriate. This is a small, self-contained documentation fix. The code example corrections are verifiably correct: "bytes=0-100".split("=") does produce ["bytes", "0-100"], and Blob.slice() is indeed end-exclusive while HTTP Range headers are end-inclusive.

Other factors

CodeRabbit raised a valid concern about open-ended Range headers, which was addressed in the follow-up commit (f7e3bcd). That inline comment is now marked as resolved. No outstanding reviewer comments remain. The changes are minimal, correct, and improve the accuracy of the documentation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/runtime/http/routing.mdx`:
- Around line 221-226: The snippet that parses the Range header calls
req.headers.get("Range") and immediately chains .split, which throws when the
header is absent; update the code around the Range parsing (the expression that
produces startStr and endStr from req.headers.get("Range")) to first guard for a
missing header (e.g., const range = req.headers.get("Range"); if (!range) { /*
return 200/handle full response or document requirement */ } ), then perform the
split on range (or provide a safe default like "0-") so startStr/endStr parsing
never runs on null; alternatively explicitly document that the example requires
the Range header be present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 111594c5-7c7c-4c12-bc1f-7c17f1e4893b

📥 Commits

Reviewing files that changed from the base of the PR and between f7e3bcd and 62a8ea9.

📒 Files selected for processing (1)
  • docs/runtime/http/routing.mdx

Return the full file when the Range header is absent, avoiding a
TypeError from chaining .split() on null.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — straightforward docs fix correcting an off-by-one and a misleading comment in the Range request example.

Extended reasoning...

Overview

This PR modifies a single documentation file (docs/runtime/http/routing.mdx) to fix two issues in a Range request code example: (1) a wrong inline comment claiming .split("=") produces ["Range: bytes", "0-100"] when it actually produces ["bytes", "0-100"], and (2) an off-by-one bug where slice(start, end) was used directly with the inclusive Range end value instead of slice(start, end + 1). The fix also properly handles open-ended ranges (bytes=N-) by conditionally passing undefined as the slice end.

Security risks

None. This is a documentation-only change with no runtime code modifications.

Level of scrutiny

Low scrutiny is appropriate. This is a small, self-contained fix to a code example in documentation. The logic is straightforward and correct — HTTP Range headers use inclusive end bounds while Blob.slice() uses exclusive end bounds, so +1 is the right adjustment. The open-ended range handling (endStr ? Number(endStr) + 1 : undefined) is also correct.

Other factors

CodeRabbit raised a valid point about suffix byte ranges (bytes=-500) not being handled, but the PR explicitly scopes the example to bytes=N-M and bytes=N- forms via a comment, which is reasonable for a documentation snippet. The first CodeRabbit issue (open-ended ranges) was addressed in a follow-up commit. CI passes. No outstanding reviewer comments requiring action.

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — straightforward docs fix correcting the Range request example.

Extended reasoning...

Overview

This PR modifies a single documentation file (docs/runtime/http/routing.mdx) to fix an incorrect Range request code example. The changes correct an off-by-one error (HTTP Range headers are end-inclusive but Blob.slice() is end-exclusive), fix a misleading inline comment, add a null guard for when the Range header is absent, and properly handle open-ended ranges (bytes=N-).

Security risks

None. This is a documentation-only change with no impact on runtime code, build artifacts, or any executable paths.

Level of scrutiny

Low scrutiny is appropriate. This is a small, self-contained fix to a code example in documentation. The corrections are factually verifiable (Number("") === 0, slice() is end-exclusive, .split("=") on "bytes=0-100" yields ["bytes", "0-100"]). The build passes and CodeRabbit found no remaining actionable issues.

Other factors

The PR went through multiple iterations addressing CodeRabbit feedback (open-ended range handling, missing header guard), all of which were resolved. There is one unresolved CodeRabbit comment about suffix byte ranges (bytes=-500), but this is a nice-to-have edge case for a documentation example, not a correctness bug in the changes made. The final code is clear, well-commented, and more correct than the original.

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.

Range request example is off-by-one

1 participant