Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
804b6f22
Unverified
Commit
804b6f22
authored
Feb 03, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 03, 2026
Browse files
Merge pull request #468 from s-Joshua-s/fix/thinking-block-modification-error
fix(api): 修复 thinking 块被意外修改导致的 400 错误
parents
cb58daf3
ad90bb46
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/internal/service/antigravity_gateway_service.go
View file @
804b6f22
...
...
@@ -1168,6 +1168,12 @@ func isSignatureRelatedError(respBody []byte) bool {
return
true
}
// Detect thinking block modification errors:
// "thinking or redacted_thinking blocks in the latest assistant message cannot be modified"
if
strings
.
Contains
(
msg
,
"cannot be modified"
)
&&
(
strings
.
Contains
(
msg
,
"thinking"
)
||
strings
.
Contains
(
msg
,
"redacted_thinking"
))
{
return
true
}
return
false
}
...
...
backend/internal/service/gateway_service.go
View file @
804b6f22
...
...
@@ -603,12 +603,18 @@ func (s *GatewayService) hashContent(content string) string {
}
// replaceModelInBody 替换请求体中的model字段
// 使用 json.RawMessage 保留其他字段的原始字节,避免 thinking 块等内容被修改
func
(
s
*
GatewayService
)
replaceModelInBody
(
body
[]
byte
,
newModel
string
)
[]
byte
{
var
req
map
[
string
]
any
var
req
map
[
string
]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
body
,
&
req
);
err
!=
nil
{
return
body
}
req
[
"model"
]
=
newModel
// 只序列化 model 字段
modelBytes
,
err
:=
json
.
Marshal
(
newModel
)
if
err
!=
nil
{
return
body
}
req
[
"model"
]
=
modelBytes
newBody
,
err
:=
json
.
Marshal
(
req
)
if
err
!=
nil
{
return
body
...
...
@@ -805,12 +811,21 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
if
len
(
body
)
==
0
{
return
body
,
modelID
,
nil
}
// 使用 json.RawMessage 保留 messages 的原始字节,避免 thinking 块被修改
var
reqRaw
map
[
string
]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
body
,
&
reqRaw
);
err
!=
nil
{
return
body
,
modelID
,
nil
}
// 同时解析为 map[string]any 用于修改非 messages 字段
var
req
map
[
string
]
any
if
err
:=
json
.
Unmarshal
(
body
,
&
req
);
err
!=
nil
{
return
body
,
modelID
,
nil
}
toolNameMap
:=
make
(
map
[
string
]
string
)
modified
:=
false
if
system
,
ok
:=
req
[
"system"
];
ok
{
switch
v
:=
system
.
(
type
)
{
...
...
@@ -818,6 +833,7 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
sanitized
:=
sanitizeSystemText
(
v
)
if
sanitized
!=
v
{
req
[
"system"
]
=
sanitized
modified
=
true
}
case
[]
any
:
for
_
,
item
:=
range
v
{
...
...
@@ -835,6 +851,7 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
sanitized
:=
sanitizeSystemText
(
text
)
if
sanitized
!=
text
{
block
[
"text"
]
=
sanitized
modified
=
true
}
}
}
...
...
@@ -845,6 +862,7 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
if
normalized
!=
rawModel
{
req
[
"model"
]
=
normalized
modelID
=
normalized
modified
=
true
}
}
...
...
@@ -860,16 +878,19 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
normalized
:=
normalizeToolNameForClaude
(
name
,
toolNameMap
)
if
normalized
!=
""
&&
normalized
!=
name
{
toolMap
[
"name"
]
=
normalized
modified
=
true
}
}
if
desc
,
ok
:=
toolMap
[
"description"
]
.
(
string
);
ok
{
sanitized
:=
sanitizeToolDescription
(
desc
)
if
sanitized
!=
desc
{
toolMap
[
"description"
]
=
sanitized
modified
=
true
}
}
if
schema
,
ok
:=
toolMap
[
"input_schema"
];
ok
{
normalizeToolInputSchema
(
schema
,
toolNameMap
)
modified
=
true
}
tools
[
idx
]
=
toolMap
}
...
...
@@ -898,11 +919,15 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
normalizedTools
[
normalized
]
=
value
}
req
[
"tools"
]
=
normalizedTools
modified
=
true
}
}
else
{
req
[
"tools"
]
=
[]
any
{}
modified
=
true
}
// 处理 messages 中的 tool_use 块,但保留包含 thinking 块的消息的原始字节
messagesModified
:=
false
if
messages
,
ok
:=
req
[
"messages"
]
.
([]
any
);
ok
{
for
_
,
msg
:=
range
messages
{
msgMap
,
ok
:=
msg
.
(
map
[
string
]
any
)
...
...
@@ -913,6 +938,24 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
if
!
ok
{
continue
}
// 检查此消息是否包含 thinking 块
hasThinking
:=
false
for
_
,
block
:=
range
content
{
blockMap
,
ok
:=
block
.
(
map
[
string
]
any
)
if
!
ok
{
continue
}
blockType
,
_
:=
blockMap
[
"type"
]
.
(
string
)
if
blockType
==
"thinking"
||
blockType
==
"redacted_thinking"
{
hasThinking
=
true
break
}
}
// 如果包含 thinking 块,跳过此消息的修改
if
hasThinking
{
continue
}
// 只修改不包含 thinking 块的消息中的 tool_use
for
_
,
block
:=
range
content
{
blockMap
,
ok
:=
block
.
(
map
[
string
]
any
)
if
!
ok
{
...
...
@@ -925,6 +968,7 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
normalized
:=
normalizeToolNameForClaude
(
name
,
toolNameMap
)
if
normalized
!=
""
&&
normalized
!=
name
{
blockMap
[
"name"
]
=
normalized
messagesModified
=
true
}
}
}
...
...
@@ -934,6 +978,7 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
if
opts
.
stripSystemCacheControl
{
if
system
,
ok
:=
req
[
"system"
];
ok
{
_
=
stripCacheControlFromSystemBlocks
(
system
)
modified
=
true
}
}
...
...
@@ -945,12 +990,46 @@ func normalizeClaudeOAuthRequestBody(body []byte, modelID string, opts claudeOAu
}
if
existing
,
ok
:=
metadata
[
"user_id"
]
.
(
string
);
!
ok
||
existing
==
""
{
metadata
[
"user_id"
]
=
opts
.
metadataUserID
modified
=
true
}
}
delete
(
req
,
"temperature"
)
delete
(
req
,
"tool_choice"
)
if
_
,
hasTemp
:=
req
[
"temperature"
];
hasTemp
{
delete
(
req
,
"temperature"
)
modified
=
true
}
if
_
,
hasChoice
:=
req
[
"tool_choice"
];
hasChoice
{
delete
(
req
,
"tool_choice"
)
modified
=
true
}
if
!
modified
&&
!
messagesModified
{
return
body
,
modelID
,
toolNameMap
}
// 如果 messages 没有被修改,保留原始 messages 字节
if
!
messagesModified
{
// 序列化非 messages 字段
newBody
,
err
:=
json
.
Marshal
(
req
)
if
err
!=
nil
{
return
body
,
modelID
,
toolNameMap
}
// 替换回原始的 messages
var
newReq
map
[
string
]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
newBody
,
&
newReq
);
err
!=
nil
{
return
newBody
,
modelID
,
toolNameMap
}
if
origMessages
,
ok
:=
reqRaw
[
"messages"
];
ok
{
newReq
[
"messages"
]
=
origMessages
}
finalBody
,
err
:=
json
.
Marshal
(
newReq
)
if
err
!=
nil
{
return
newBody
,
modelID
,
toolNameMap
}
return
finalBody
,
modelID
,
toolNameMap
}
// messages 被修改了,需要完整序列化
newBody
,
err
:=
json
.
Marshal
(
req
)
if
err
!=
nil
{
return
body
,
modelID
,
toolNameMap
...
...
@@ -3672,6 +3751,13 @@ func (s *GatewayService) isThinkingBlockSignatureError(respBody []byte) bool {
return
true
}
// 检测 thinking block 被修改的错误
// 例如: "thinking or redacted_thinking blocks in the latest assistant message cannot be modified"
if
strings
.
Contains
(
msg
,
"cannot be modified"
)
&&
(
strings
.
Contains
(
msg
,
"thinking"
)
||
strings
.
Contains
(
msg
,
"redacted_thinking"
))
{
log
.
Printf
(
"[SignatureCheck] Detected thinking block modification error"
)
return
true
}
// 检测空消息内容错误(可能是过滤 thinking blocks 后导致的)
// 例如: "all messages must have non-empty content"
if
strings
.
Contains
(
msg
,
"non-empty content"
)
||
strings
.
Contains
(
msg
,
"empty content"
)
{
...
...
backend/internal/service/identity_service.go
View file @
804b6f22
...
...
@@ -169,22 +169,31 @@ func (s *IdentityService) ApplyFingerprint(req *http.Request, fp *Fingerprint) {
// RewriteUserID 重写body中的metadata.user_id
// 输入格式:user_{clientId}_account__session_{sessionUUID}
// 输出格式:user_{cachedClientID}_account_{accountUUID}_session_{newHash}
//
// 重要:此函数使用 json.RawMessage 保留其他字段的原始字节,
// 避免重新序列化导致 thinking 块等内容被修改。
func
(
s
*
IdentityService
)
RewriteUserID
(
body
[]
byte
,
accountID
int64
,
accountUUID
,
cachedClientID
string
)
([]
byte
,
error
)
{
if
len
(
body
)
==
0
||
accountUUID
==
""
||
cachedClientID
==
""
{
return
body
,
nil
}
//
解析JSON
var
reqMap
map
[
string
]
any
//
使用 RawMessage 保留其他字段的原始字节
var
reqMap
map
[
string
]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
body
,
&
reqMap
);
err
!=
nil
{
return
body
,
nil
}
metadata
,
ok
:=
reqMap
[
"metadata"
]
.
(
map
[
string
]
any
)
// 解析 metadata 字段
metadataRaw
,
ok
:=
reqMap
[
"metadata"
]
if
!
ok
{
return
body
,
nil
}
var
metadata
map
[
string
]
any
if
err
:=
json
.
Unmarshal
(
metadataRaw
,
&
metadata
);
err
!=
nil
{
return
body
,
nil
}
userID
,
ok
:=
metadata
[
"user_id"
]
.
(
string
)
if
!
ok
||
userID
==
""
{
return
body
,
nil
...
...
@@ -207,7 +216,13 @@ func (s *IdentityService) RewriteUserID(body []byte, accountID int64, accountUUI
newUserID
:=
fmt
.
Sprintf
(
"user_%s_account_%s_session_%s"
,
cachedClientID
,
accountUUID
,
newSessionHash
)
metadata
[
"user_id"
]
=
newUserID
reqMap
[
"metadata"
]
=
metadata
// 只重新序列化 metadata 字段
newMetadataRaw
,
err
:=
json
.
Marshal
(
metadata
)
if
err
!=
nil
{
return
body
,
nil
}
reqMap
[
"metadata"
]
=
newMetadataRaw
return
json
.
Marshal
(
reqMap
)
}
...
...
@@ -215,6 +230,9 @@ func (s *IdentityService) RewriteUserID(body []byte, accountID int64, accountUUI
// RewriteUserIDWithMasking 重写body中的metadata.user_id,支持会话ID伪装
// 如果账号启用了会话ID伪装(session_id_masking_enabled),
// 则在完成常规重写后,将 session 部分替换为固定的伪装ID(15分钟内保持不变)
//
// 重要:此函数使用 json.RawMessage 保留其他字段的原始字节,
// 避免重新序列化导致 thinking 块等内容被修改。
func
(
s
*
IdentityService
)
RewriteUserIDWithMasking
(
ctx
context
.
Context
,
body
[]
byte
,
account
*
Account
,
accountUUID
,
cachedClientID
string
)
([]
byte
,
error
)
{
// 先执行常规的 RewriteUserID 逻辑
newBody
,
err
:=
s
.
RewriteUserID
(
body
,
account
.
ID
,
accountUUID
,
cachedClientID
)
...
...
@@ -227,17 +245,23 @@ func (s *IdentityService) RewriteUserIDWithMasking(ctx context.Context, body []b
return
newBody
,
nil
}
//
解析重写后的 body,提取 user_id
var
reqMap
map
[
string
]
any
//
使用 RawMessage 保留其他字段的原始字节
var
reqMap
map
[
string
]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
newBody
,
&
reqMap
);
err
!=
nil
{
return
newBody
,
nil
}
metadata
,
ok
:=
reqMap
[
"metadata"
]
.
(
map
[
string
]
any
)
// 解析 metadata 字段
metadataRaw
,
ok
:=
reqMap
[
"metadata"
]
if
!
ok
{
return
newBody
,
nil
}
var
metadata
map
[
string
]
any
if
err
:=
json
.
Unmarshal
(
metadataRaw
,
&
metadata
);
err
!=
nil
{
return
newBody
,
nil
}
userID
,
ok
:=
metadata
[
"user_id"
]
.
(
string
)
if
!
ok
||
userID
==
""
{
return
newBody
,
nil
...
...
@@ -278,7 +302,13 @@ func (s *IdentityService) RewriteUserIDWithMasking(ctx context.Context, body []b
)
metadata
[
"user_id"
]
=
newUserID
reqMap
[
"metadata"
]
=
metadata
// 只重新序列化 metadata 字段
newMetadataRaw
,
marshalErr
:=
json
.
Marshal
(
metadata
)
if
marshalErr
!=
nil
{
return
newBody
,
nil
}
reqMap
[
"metadata"
]
=
newMetadataRaw
return
json
.
Marshal
(
reqMap
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment