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

  1. Use exactly the supported guard prefix "while(1);" immediately followed by valid JSON, or emit no prefix at all
  2. Verify the payload with json.Valid on both the raw and trimmed forms before returning it
  3. Remove any custom/legacy security prefixes such as ")]}'",\n" that this validator does not accept
  4. 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

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


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)