everything/tasks: tasks/update checks MAX_ANSWER_BYTES against string length, so non-ASCII answers exceed the documented byte budget
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 1/5
- 見積もり時間
- 1時間未満
- 初心者へのやさしさ
- 78/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- typescript
- 領域
- backend
調査の方向性
サイズチェックは src/everything/server/tasks.ts の tasks/update ハンドラ内、411〜424行目付近にあり、JSON.stringify(answered).length が MAX_ANSWER_BYTES と比較されています。このブロックを読んでから、é を33,000文字含む回答を送信して受け入れられることを確認し、問題を再現してください。測定値がUTF-8バイト数を数え、そのペイロードが -32602 エラーを返し、タスクが変更されないままであれば完了です。
索引モデルが issue の本文から書いたものです。
説明
| Field | Value |
|---|---|
| Labels (suggested) | bug, everything, tasks |
| Reproduced on | commit 163a5e935756 (default branch at the time of analysis) |
| Introduced by | commit 27b2d2415cdc0ad13782edb8b6c3cecd7846b78a — "feat(everything): serve both protocol eras and demonstrate …" |
Summary
The everything server's tasks/update handler limits the size of client-supplied answers with
MAX_ANSWER_BYTES, but computes the size with JSON.stringify(answered).length. In JavaScript that is
the number of UTF-16 code units, not bytes. For any non-ASCII answer the guard under-counts by up to
3× and lets a payload that is larger than the documented byte budget through, after which the oversized
answers are handed to task.behavior.receive() and retained in the task.
Affected code
src/everything/server/tasks.ts, lines 411–424 at 163a5e935756:
const answered = Object.fromEntries(
Object.entries(inputResponses).filter(([key]) =>
outstanding.includes(key),
),
);
const size = JSON.stringify(answered).length; // <- UTF-16 code units, not bytes
if (size > MAX_ANSWER_BYTES) {
throw new ProtocolError(
ProtocolErrorCode.InvalidParams,
`Invalid params for tasks/update: the answers are ${size} bytes, over the ${MAX_ANSWER_BYTES}-byte limit; the task is unchanged`,
);
}
if (Object.keys(answered).length > 0) {
task.behavior.receive(answered, now);
}
The constant is named MAX_ANSWER_BYTES and the error message promises a byte comparison
("the answers are N bytes"), so the unit mismatch is internal to the handler, not a documentation
question.
Expected behavior
tasks/update rejects the request with -32602 Invalid params because the serialized answers are
66 012 bytes, which is over the 65 536-byte budget; the task is left unchanged.
Actual behavior
Calling tasks/update with one outstanding answer whose text is 33 000 é characters, the handler accepts the payload and retains it in the task (MAX_ANSWER_BYTES = 65536).
Payload: one outstanding answer whose text is 33 000 U+00E9 (é) characters.
MAX_ANSWER_BYTES = 65536.
{"mode":"bug","outcome":{"accepted":true,"received":66012}}
TASKS_UPDATE_BYTE_BUDGET_BYPASSED
JSON.stringify(...).length= 33 012 → passes the guard.- UTF-8
Buffer.byteLength(..., "utf8")= 66 012 → 476 bytes over the limit. - The task retained the answer (
received: 66012), so the documented limit was not enforced.
Control: the same probe against a copy of tasks.ts with line 416 changed to
Buffer.byteLength(JSON.stringify(answered), "utf8") produces
{"mode":"control","outcome":{"accepted":false,
"error":"-32602: Invalid params for tasks/update: the answers are 66012 bytes, over the 65536-byte limit; the task is unchanged"}}
CONTROL_REJECTED_BY_BYTES
and retained is undefined — the divergence disappears with that one line.
Why it matters
- The limit is a resource guard for retained task state (the answer is stored until the task settles).
Non-ASCII answers — ordinary for any non-English client, or for base64/emoji content — get up to a 3×
larger effective budget. 33 000 characters of CJK/Cyrillic/emoji-style text becomes ~99 KB against a
stated 64 KiB ceiling. - The rejection message itself reports a byte count that is not a byte count, which makes the failure
mode confusing for client authors.
Suggested fix
Use a byte-accurate measure:
const size = Buffer.byteLength(JSON.stringify(answered), "utf8");
(new TextEncoder().encode(...).length if the file must stay runtime-agnostic). Note that both forms
count the UTF-8 serialization of the JSON text, which is what the limit documents.
- 主要言語
- TypeScript
- スター
- 91.7k
- フォーク
- 11.9k
- 平均マージ
- 3時間 30分
- マージ済み PR(30日)
- 204
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
modelcontextprotocol/servers のほかの issue
-
bug v2
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
modelcontextprotocol/servers#5483 ·
メンテナーはふだん 1 日以内に返信
-
bug v2
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
modelcontextprotocol/servers#5482 ·
メンテナーはふだん 1 日以内に返信
-
chore dependencies server-filesystem v2
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
modelcontextprotocol/servers#5481 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
chore v2
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
modelcontextprotocol/servers#5463 ·
メンテナーはふだん 1 日以内に返信
-
bug server-filesystem v2
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
modelcontextprotocol/servers#5260 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
modelcontextprotocol/servers の issue をすべて見る
似ている issue
-
Chip source filter and denial markers miss Storage rows when a Storage activity source is pickedオープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
omdsh-dev/DSH-better-sidebar#1141 ·
メンテナーはふだん 2 日以内に返信
-
desktop linux photos
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
Cleanup Setup対応中かも @AlexanderKaran が今日担当しました。 オープンdocumentation enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
e18e/framework-tracker#769 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信