siyuan-note/siyuan · error
invalid plugin secure JSON response
Error message
invalid plugin secure JSON response
What it means
Endpoints declared with PluginServiceSecureJSON may return either raw JSON or JSON prefixed with the JSON-hijacking guard "while(1);". ValidatePluginServiceResponse accepts the payload only if the raw bytes are valid JSON or the bytes remain valid JSON after stripping that exact prefix; otherwise it returns "invalid plugin secure JSON response". The error means the secure-JSON contract is violated — the client would neither parse it directly nor after removing the guard prefix.
Solutions
- Use exactly the supported guard prefix "while(1);" immediately followed by valid JSON, or emit no prefix at all
- Verify the payload with json.Valid on both the raw and trimmed forms before returning it
- Remove any custom/legacy security prefixes such as ")]}'",\n" that this validator does not accept
- Check for truncated or duplicated writes in the handler that corrupt the JSON body
Example fix
// before
w.Write([]byte(")]}'\"\n" + string(badJSON)))
// after
inner, _ := json.Marshal(data)
w.Write([]byte("while(1);" + string(inner))) Defensive patterns
Strategy: validation
Validate before calling
func isValidSecureJSON(payload []byte) bool {
return json.Valid(payload) || json.Valid([]byte(strings.TrimPrefix(string(payload), "while(1);")))
} Try / catch
if err := bundle.ValidatePluginServiceResponse(method, path, PluginServiceSecureJSON, status, ct, payload); err != nil { if strings.Contains(err.Error(), "invalid plugin secure JSON response") { fixPrefixAndRevalidate(payload); return }; return err } Prevention
- Use exactly the "while(1);" prefix (with semicolon) when enabling the hijacking guard
- Marshal with json.Marshal before prepending the prefix
- Avoid legacy prefixes like ")]}'" that this contract does not accept
- Add a golden-file test for secure JSON responses
When it happens
Trigger: Calling Bundle.ValidatePluginServiceResponse with mode PluginServiceSecureJSON and a payload that fails json.Valid both as-is and after strings.TrimPrefix of "while(1);" — e.g. a different guard prefix, concatenated JSON values, or truncated output.
Common situations: The handler writes a custom anti-hijack prefix (e.g. ")]}'",\n" or "while(1)" without the semicolon) that the validator does not recognize; the response is truncated; two JSON documents are written back to back; an error page replaces the JSON body.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- invalid plugin JSON response
- invalid plugin JSONP response
- Argon2id Iterations too low (minimum 3)
- asset path escapes data directory
- asset path escapes data directory
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/b6d15f2360e52f72.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/plugin_service_protocol.go:226
if !json.Valid(payload) {
return fmt.Errorf("invalid plugin JSON response")
}
case PluginServiceJSONP:
valid := json.Valid(payload)
if tail, ok := strings.CutSuffix(string(payload), ");"); ok {
for index, char := range tail {
if char == '(' && json.Valid([]byte(tail[index+1:])) {
valid = true
break
}
}
}
if !valid {
return fmt.Errorf("invalid plugin JSONP response")
}
case PluginServiceSecureJSON:
if !json.Valid(payload) && !json.Valid([]byte(strings.TrimPrefix(string(payload), "while(1);"))) {
return fmt.Errorf("invalid plugin secure JSON response")
}
case PluginServiceWebSocket:
if status == 101 && len(payload) != 0 {
return fmt.Errorf("WebSocket handshake contains a body")
}
case PluginServiceXML:
decoder := xml.NewDecoder(strings.NewReader(string(payload)))
for {
if _, err := decoder.Token(); err != nil {
if err == io.EOF {
break
}
return err
}
}
case PluginServiceYAML:
var value yaml.Node
if err := yaml.Unmarshal(payload, &value); err != nil {View on GitHub (pinned to 9f775e8a12)