on-stop.sh: byte-level truncation splits multibyte UTF-8, Stop hook fails Claude Code JSON validation
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
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
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của warpdotdev/claude-code-warp
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 85/100
warpdotdev/claude-code-warp#96 · 2 bình luận ·
-
on-stop.sh slurps the entire transcript with jq -s every turn — ~1 GB RSS on long sessions, triggered a host OOMCó thể đã có người làm @GeiserX đã nhận 25 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
warpdotdev/claude-code-warp#90 · 1 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
warpdotdev/claude-code-warp#63 · 2 bình luận ·
Tất cả issue của warpdotdev/claude-code-warp
Issue tương tự
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 82/100
suitenumerique/find#205 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
README em inglêsĐang mởgood first issue help wanted priority:p2 type:docs
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
lucacasanova/homedeck#122 ·
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày