Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

es-client: non-JSON error responses report "Body is unusable" instead of the HTTP status

オープン 初心者向け
#686 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
75/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
nodejs, typescript

調査の方向性

バグは src/lib/es-client.ts の 125-132 行目にあります。まずファイルを読み、リクエストフローを理解してください。修正方法は、kibana-client.ts や cloud-client.ts が処理するのと同様に、レスポンスボディをまずテキストとして読み取り、その後 JSON として解析を試みることです。非 JSON のエラーレスポンスを返すモック HTTP サーバーを使用してエラーを再現するテストを作成し、修正が正しいステータスコードとボディを返すことを確認してください。

索引モデルが issue の本文から書いたものです。

説明

Summary

Any non-2xx response whose body is not valid JSON surfaces as Error: Body is unusable: Body has already been read, losing the status code. That is undici's TypeError — the error branch of
EsClient.request reads the body twice. Deterministic, not timing-dependent.

The usual way to hit it is a reverse proxy in front of Elasticsearch: proxy_read_timeout expiring
on a long ES|QL query returns an nginx HTML 504, and the CLI reports Body is unusable instead of
504 — pointing suspicion at the client rather than the proxy.

Root cause

src/lib/es-client.ts#L125-L132:

if (!response.ok) {
  let body: unknown
  try {
    body = await response.json()   // consumes the stream
  } catch {
    body = await response.text()   // ← throws: body already consumed
  }
  throw new EsResponseError(response.status, body)
}

response.json() disturbs the body even when it rejects, so the catch branch can never read it.

Reproduction

cat > elasticrc-local.yml <<'YAML'
current_context: local
contexts:
  local:
    elasticsearch:
      url: http://127.0.0.1:9999
      auth: { api_key: ZmFrZTpmYWtl }
YAML
chmod 0600 elasticrc-local.yml
export ELASTIC_CLI_CONFIG_FILE="$PWD/elasticrc-local.yml" NO_PROXY=localhost,127.0.0.1

# stand in for "ES behind a proxy that timed out"
node -e 'require("http").createServer((q,s)=>{s.writeHead(504,{"content-type":"text/html"});s.end("<html><body>504 Gateway Time-out</body></html>")}).listen(9999,"127.0.0.1")' &

elastic es esql query --query 'FROM x | LIMIT 1' --format txt
# Error: Body is unusable: Body has already been read     (exit 1)

Same for 502, 503, text/plain and empty bodies. A JSON error body works correctly
(Error: illegal_argument_exception: …). Reproduced on v0.4.0 and v0.5.0, Node v26.8.1,
Linux x86_64, against Elasticsearch 9.5.3.

Suggested fix

Read once, then parse — as kibana-client.ts#L212
and cloud-client.ts#L75
already do in this repo:

const text = await response.text()
let body: unknown
try {
  body = JSON.parse(text)
} catch {
  body = text
}
throw new EsResponseError(response.status, body)

EsResponseError's message should also carry the status, since a string body currently renders as
bare HTML. Happy to open a PR with tests for an HTML and an empty error body.

Context

Introduced by #281 (native-fetch EsClient); @elastic/transport handled it correctly. Present in
every release since v0.2.0 and on main today. #588 fixed the same blind-JSON.parse mistake on
the success branch of this file — this is the error branch.

主要言語
TypeScript
スター
43
フォーク
24
平均マージ
1日 5時間
マージ済み PR(30日)
61

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

elastic/cli のほかの issue

elastic/cli の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。