● API Error: 400 1 validation error: {'type': 'literal_error', 'loc': ('body', 'messages', 1, 'role'), 'msg': "Input should be 'user' or 'assistant'", 'input': 'system', 'ctx': {'expected': "'user' or 'assistant'"}}
File "/usr/local/lib/python3.12/dist-packages/vllm/entrypoints/utils.py", line 48, in create_messages POST /v1/messages [{'type': 'literal_error', 'loc': ('body', 'messages', 1, 'role'), 'msg': "Input should be 'user' or 'assistant'", 'input': 'system', 'ctx': {'expected': "'user' or 'assistant'"}}]
这是 Claude Code v2.1.154+ 的已知回归问题(regression)。该版本开始将 system 角色放入 messages 数组内部,而 vLLM 的 Anthropic 兼容层只接受 user 和 assistant 两种角色,导致 400 校验错误。
问题根因
Anthropic Messages API 的标准做法是将系统提示放在顶层 system 字段,而非 messages 数组中。但 Claude Code v2.1.154+ 在对话过程中会将 system 角色作为消息数组的一员发送(通常出现在 messages[1] 位置),这导致:
export http_proxy=http://127.0.0.1:26890 export http_proxy=https://127.0.0.1:26890 # Parse command line arguments TARGET="$1" # Optional target parameter # Validate target if provided if [[ -n "$TARGET" ]] && [[ ! "$TARGET" =~ ^(stable|latest|[0-9]+\.[0-9]+\.[0-9]+(-[^[:space:]]+)?)$ ]]; then echo "Usage: $0 [stable|latest|VERSION]" >&2 exit 1 fi
DOWNLOAD_BASE_URL="https://downloads.claude.ai/claude-code-releases" DOWNLOAD_DIR="$HOME/.claude/downloads" # Check for required dependencies DOWNLOADER="" if command -v curl >/dev/null 2>&1; then DOWNLOADER="curl" elif command -v wget >/dev/null 2>&1; then DOWNLOADER="wget" else echo "Either curl or wget is required but neither is installed" >&2 exit 1 fi # Check if jq is available (optional) HAS_JQ=false if command -v jq >/dev/null 2>&1; then HAS_JQ=true fi # Download function that works with both curl and wget # download_file() { # local url="$1" # local output="$2" # if [ "$DOWNLOADER" = "curl" ]; then # if [ -n "$output" ]; then # curl -fsSL -o "$output""$url" # else # curl -fsSL "$url" # fi # elif [ "$DOWNLOADER" = "wget" ]; then # if [ -n "$output" ]; then # wget -q -O "$output""$url" # else # wget -q -O - "$url" # fi # else # return 1 # fi # } download_file() { local url="$1" local output="$2"
if [ "$DOWNLOADER" = "curl" ]; then if [ -n "$output" ]; then curl -fL -# -o "$output" "$url" else curl -fL -# "$url" fi elif [ "$DOWNLOADER" = "wget" ]; then if [ -n "$output" ]; then wget --show-progress -O "$output" "$url" else wget -q -O - "$url" fi else return 1 fi } # Simple JSON parser for extracting checksum when jq is not available get_checksum_from_manifest() { local json="$1" local platform="$2"
# Normalize JSON to single line and extract checksum json=$(echo "$json" | tr -d '\n\r\t' | sed 's/ \+/ /g')
# Extract checksum for platform using bash regex if [[ $json =~ \"$platform\"[^}]*\"checksum\"[[:space:]]*:[[:space:]]*\"([a-f0-9]{64})\" ]]; then echo "${BASH_REMATCH[1]}" return 0 fi
return 1 } # Detect platform case "$(uname -s)" in Darwin) os="darwin" ;; Linux) os="linux" ;; MINGW*|MSYS*|CYGWIN*) echo "Windows is not supported by this script. See https://code.claude.com/docs for installation options." >&2; exit 1 ;; *) echo "Unsupported operating system: $(uname -s). See https://code.claude.com/docs for supported platforms." >&2; exit 1 ;; esac
case "$(uname -m)" in x86_64|amd64) arch="x64" ;; arm64|aarch64) arch="arm64" ;; *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; esac # Detect Rosetta 2 on macOS: if the shell is running as x64 under Rosetta on an ARM Mac, # download the native arm64 binary instead of the x64 one if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then if [ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" = "1" ]; then arch="arm64" fi fi # Check for musl on Linux and adjust platform accordingly if [ "$os" = "linux" ]; then if [ -f /lib/libc.musl-x86_64.so.1 ] || [ -f /lib/libc.musl-aarch64.so.1 ] || ldd /bin/ls 2>&1 | grep -q musl; then platform="linux-${arch}-musl" else platform="linux-${arch}" fi else platform="${os}-${arch}" fi mkdir -p "$DOWNLOAD_DIR" # Always download latest version (which has the most up-to-date installer) # version=$(download_file "$DOWNLOAD_BASE_URL/latest") if [ -n "$TARGET" ] && [[ "$TARGET" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then version="$TARGET" else version=$(download_file "$DOWNLOAD_BASE_URL/latest") fi
echo "Detected platform: $platform" echo "Latest version: $version" # Reject non-version content (e.g. an HTML error page) before it reaches the manifest URL if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then echo "Failed to get a valid version from downloads.claude.ai (got unexpected content)." >&2 echo "This can happen if the download service is unreachable or not available in your region - see https://www.anthropic.com/supported-countries" >&2 exit 1 fi # Download manifest and extract checksum manifest_json=$(download_file "$DOWNLOAD_BASE_URL/$version/manifest.json") echo "Downloaded manifest for version $version" # Use jq if available, otherwise fall back to pure bash parsing if [ "$HAS_JQ" = true ]; then checksum=$(echo "$manifest_json" | jq -r ".platforms[\"$platform\"].checksum // empty") else checksum=$(get_checksum_from_manifest "$manifest_json" "$platform") fi # Validate checksum format (SHA256 = 64 hex characters) if [ -z "$checksum" ] || [[ ! "$checksum" =~ ^[a-f0-9]{64}$ ]]; then echo "Platform $platform not found in manifest" >&2 exit 1 fi
echo "Checksum for platform $platform: $checksum" # Download and verify binary_path="$DOWNLOAD_DIR/claude-$version-$platform" echo "Downloading Claude Code binary from $DOWNLOAD_BASE_URL/$version/$platform/claude..." if ! download_file "$DOWNLOAD_BASE_URL/$version/$platform/claude" "$binary_path"; then echo "Download failed" >&2 rm -f "$binary_path" exit 1 fi echo "Downloaded binary to $binary_path" # Pick the right checksum tool if [ "$os" = "darwin" ]; then actual=$(shasum -a 256 "$binary_path" | cut -d' ' -f1) else actual=$(sha256sum "$binary_path" | cut -d' ' -f1) fi
if [ "$actual" != "$checksum" ]; then echo "Checksum verification failed" >&2 rm -f "$binary_path" exit 1 fi
chmod +x "$binary_path" # Run claude install to set up launcher and shell integration echo "Setting up Claude Code..." "$binary_path" install ${TARGET:+"$TARGET"} # Clean up downloaded file rm -f "$binary_path"
$ bash install.sh 2.1.153 Detected platform: linux-x64 Latest version: 2.1.153 #################################################################################################################################################################################################################### 100.0% Downloaded manifest for version 2.1.153 Checksum for platform linux-x64: 214f603f31942162dac9a65f18d43b3ac646ae215240fad481c4aad6c60f2e38 Downloading Claude Code binary from https://downloads.claude.ai/claude-code-releases/2.1.153/linux-x64/claude... #################################################################################################################################################################################################################### 100.0% Downloaded binary to /home/wangzongwu/.claude/downloads/claude-2.1.153-linux-x64 Setting up Claude Code...
✔ Claude Code successfully installed!
Version: 2.1.153
Location: ~/.local/bin/claude
Next: Run claude --help to get started
⚠ Setup notes: ● Native installation exists but ~/.local/bin is not in your PATH. Run: