on-stop.sh: byte-level truncation splits multibyte UTF-8, Stop hook fails Claude Code JSON validation

Đang mở Phù hợp với người mới
#70 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
82/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Ít trao đổi
Công nghệ
bash
Lĩnh vực
cli, devtools

Hướng nghiên cứu

Bắt đầu trong scripts/on-stop.sh tại phần cắt ngắn QUERY và RESPONSE, sau đó kiểm tra legacy/on-stop.sh để tìm cùng cách làm. Chạy bản tái hiện việc tách tại ranh giới được cung cấp và hook đầy đủ với các locale C và UTF-8. Hoàn tất khi văn bản multibyte vẫn là UTF-8 hợp lệ và Claude Code chấp nhận JSON được phát ra.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Summary

scripts/on-stop.sh (plugin v2.1.0) truncates the extracted query/response with bash substring expansion:

QUERY="${QUERY:0:197}..."
RESPONSE="${RESPONSE:0:197}..."

Bash slices by byte, not codepoint, when the hook runs without a UTF-8 locale (Claude Code hook subprocesses commonly inherit C/POSIX). When position 197 lands inside a multibyte character - em-dashes, arrows, box-drawing characters like ⎿ that Claude Code transcripts are full of - the truncated string ends with an invalid UTF-8 fragment. That fragment goes through jq --arg into the notification payload, and Claude Code rejects the hook's stdout:

Ran 6 stop hooks
  ⎿  Stop hook error: JSON validation failed

It is intermittent by nature: it only fires when byte 197 happens to split a character, so it looks flaky and is painful to attribute (Claude Code does not say which hook failed - see anthropics/claude-code#21992 for that class).

Reproduce

# 1. Craft a transcript whose last user message has a 3-byte char straddling byte 197
python3 - <<'EOF'
import json
q = 'a'*195 + '⎿ tail of the question'      # U+23BF = 3 bytes; bytes 196-198
lines = [
    {'type':'user','message':{'content': q}},
    {'type':'assistant','message':{'content':[{'type':'text','text': 'b'*195 + '— long response text'}]}},
]
open('/tmp/repro-transcript.jsonl','w').write('\n'.join(json.dumps(l) for l in lines))
EOF

# 2. Demonstrate the corruption the hook's truncation produces under a C locale
Q=$(jq -rs '[.[] | select(.type=="user")] | last | .message.content' /tmp/repro-transcript.jsonl)
LC_ALL=C bash -c 'Q="'"$Q"'"; printf "%s" "${Q:0:197}"' | python3 -c '
import sys
sys.stdin.buffer.read().decode("utf-8")'   # -> UnicodeDecodeError: invalid continuation byte

Running the full on-stop.sh against such a transcript with LC_ALL=C (and TERM_PROGRAM=WarpTerminal + a structured-notifications-capable Warp env) emits a payload containing the broken fragment, which fails Claude Code's stop-hook stdout schema validation.

Fix

Truncate codepoint-safely. jq is already a hard dependency of the script, so the smallest change is:

# Truncate for notification display (codepoint-safe; bash ${VAR:0:N} slices
# by byte under non-UTF-8 locales and can split a multibyte character).
if [ -n "$QUERY" ] && [ ${#QUERY} -gt 200 ]; then
    QUERY="$(printf '%s' "$QUERY" | jq -Rrs '.[0:197] + "..."')"
fi
if [ -n "$RESPONSE" ] && [ ${#RESPONSE} -gt 200 ]; then
    RESPONSE="$(printf '%s' "$RESPONSE" | jq -Rrs '.[0:197] + "..."')"
fi

Verified locally (patched plugin cache): valid JSON output across repeated runs under both UTF-8 and C locales, including the crafted boundary-splitting transcript above; the per-turn "JSON validation failed" error disappeared.

An equivalent guard likely belongs in legacy/on-stop.sh too if it shares the truncation idiom.

Environment

  • claude-code-warp plugin 2.1.0 (marketplace install)
  • Claude Code 2.1.197, macOS (darwin 25.5.0), bash 3.2 shebang path
  • Related-but-distinct: #63 (CRLF), #67 (notification spam) - same script, different bugs
Ngôn ngữ chính
Shell
Star
237
Fork
62
Merge trung bình
18 phút
Pull request đã merge (30 ngày)
1

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của warpdotdev/claude-code-warp

Tất cả issue của warpdotdev/claude-code-warp

Issue tương tự

Thêm issue về Shell/Bash

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.