OpenBMB / OpenBMB/PilotDeck

Tool result size limit can return output larger than maxBytes for small limits

Open Beginner friendly
#426 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
4k
Forks
453
Avg merge
12h 30m
Merged PRs (30d)
46

Description

Tool result size limit can return output larger than maxBytes for small limits

Summary

applyResultSizeLimit() treats maxBytes as disabled only when it is undefined or negative, so 0 and other small non-negative values are valid limits. When the limit is smaller than the fixed truncation suffix, the returned text can still contain the full suffix and exceed the requested cap.

For example, a one-byte text result with maxBytes = 0 returns a 46-byte truncation notice, and metadata.returnedBytes records 46.

Affected revision

Observed on main at 9ad10eb0e1ed75f864ca8cbda7f659f7c3b163e9.

Affected code

  • src/tool/protocol/result.ts:140-143 defines applyResultSizeLimit(content, maxBytes).
  • src/tool/protocol/result.ts:144-146 treats only undefined and negative limits as disabled.
  • src/tool/protocol/result.ts:162-167 appends the fixed truncation suffix after calculating a zero budget.

Reproduction

From the repository root:

cat > repro-tool-result-limit.mts <<'EOF'
import { applyResultSizeLimit } from "./src/tool/protocol/result.ts";

const result = applyResultSizeLimit([{ type: "text", text: " " }], 0);
const text = result.content[0]?.type === "text" ? result.content[0].text : "";
const bytes = Buffer.byteLength(text, "utf8");

console.log(JSON.stringify({ bytes, metadata: result.metadata, text }, null, 2));
EOF

pnpm exec tsx repro-tool-result-limit.mts
rm repro-tool-result-limit.mts

Observed output:

{
  "bytes": 46,
  "metadata": {
    "truncated": true,
    "originalBytes": 1,
    "returnedBytes": 46
  },
  "text": "\n[Tool output truncated: head and tail shown.]"
}

Expected behavior

When maxBytes is a non-negative limit and the original non-media content is larger than that limit, the returned text and metadata.returnedBytes should not exceed maxBytes.

For tiny limits, returning an empty string or a shortened truncation notice would both preserve the limit. The key invariant is that the visible returned content stays within the requested byte cap.

Actual behavior

The function computes:

const budget = Math.max(0, maxBytes - suffixBytes);
const truncatedText = headTailTruncateUtf8(text, budget) + suffix;

When maxBytes < suffixBytes, budget becomes 0, but the full suffix is still appended. This makes the returned text longer than the requested cap.

Impact

applyResultSizeLimit() is used for bounded tool-result previews. Default limits are larger than this minimized case, but the helper does not preserve its own boundary contract for small configured caps or future callers. The metadata also reports truncated: true while recording a returnedBytes value larger than the requested maximum.

Existing coverage

I could not find an existing issue or pull request covering this small-maxBytes result-size boundary.

Suggested fix

Apply the byte cap to the final returned string, including the suffix. In the maxBytes <= suffixBytes case, either truncate the suffix itself or return an empty preview, then set metadata.returnedBytes from the actual returned string.

Suggested regression tests:

  • maxBytes = 0
  • maxBytes = suffixBytes - 1
  • multibyte text around the boundary, to preserve UTF-8 safety

Submitted with Codex.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/tool/protocol/result.ts at applyResultSizeLimit() and inspect how the truncation suffix and returnedBytes are calculated. Search for existing result-size-limit tests, then add coverage for maxBytes values of 0, below the suffix size, and multibyte text. Done means returned content and metadata.returnedBytes never exceed a non-negative maxBytes limit while preserving UTF-8 safety.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.