{"record":{"id":"cb3679af3b4553a0","repo":"siyuan-note/siyuan","slug":"invalid-response-format-v","errorCode":null,"errorMessage":"invalid response format: %v","messagePattern":"invalid response format: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/plugin.go","lineNumber":969,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ❌ panic: invalid memory address or nil pointer dereference\n\t\t\t// response := HttpResponse{}\n\t\t\t// if err := rt.ExportTo(responseObj, &response); err != nil {\n\t\t\t// \tdone <- &ServerHandlerResult{Error: fmt.Errorf(\"invalid response format: %v\", err)}\n\t\t\t// \treturn\n\t\t\t// }\n\n\t\t\tresultJson, marshalErr := responseObj.MarshalJSON()\n\t\t\tif marshalErr != nil {\n\t\t\t\tdone <- &handleResult{Error: marshalErr}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tresponse := HttpResponse{}\n\t\t\tif unmarshalErr := json.Unmarshal(resultJson, &response); unmarshalErr != nil {\n\t\t\t\tdone <- &handleResult{Error: fmt.Errorf(\"invalid response format: %v\", unmarshalErr)}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif raw != nil && response.Body != nil && response.Body.Raw != nil {\n\t\t\t\tresponse.Body.Raw.Data = *raw\n\t\t\t}\n\n\t\t\tdone <- &handleResult{Value: &response}\n\t\t}, rt, true, handler, handlerObj, jsRequest)\n\t\treturn\n\t}, func(_ *goja.Runtime, _ any, err error) {\n\t\tif err != nil {\n\t\t\tdone <- &handleResult{Error: err}\n\t\t}\n\t})\n\tif runErr != nil {\n\t\tdone <- &handleResult{Error: runErr}\n\t}","sourceCodeStart":951,"sourceCodeEnd":987,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/plugin.go#L951-L987","documentation":"This error is returned by the kernel plugin server's HTTP request bridge (handleHttpRequest). After invoking the plugin's JS fetch handler, the returned JS object is serialized with MarshalJSON and then decoded back into the Go HttpResponse struct; if that JSON cannot be unmarshalled into the expected response shape, the kernel reports 'invalid response format'. It means the plugin handler returned an object that does not match the HttpResponse contract (body, status, headers) in a way the Go side can decode.","triggerScenarios":"A plugin's server handler (fetch-style) returns an object whose fields have types incompatible with the Go HttpResponse struct, or the MarshalJSON output contains values that fail json.Unmarshal into HttpResponse (e.g. non-numeric status, malformed body structure, body.raw.data left in an unconvertible form).","commonSituations":"Plugin authors returning a plain string or array from the handler instead of a response object; setting response.status to a string; nesting body incorrectly (e.g. body as a string instead of {raw: {...}}); returning values from async code that resolved to undefined-adjacent shapes; plugin API drift after a SiYuan version change.","solutions":["Log the exact value returned by the handler before returning it and make it a proper response object (status, headers, body fields)","Ensure response.status is a number and body follows the expected structure; for binary payloads pass body.raw.data as string|Buffer|ArrayBuffer and let the kernel extract it","Update the plugin to the current server API contract for HttpResponse (check SiYuan changelog for changes)","If the error persists, isolate the handler with a minimal {status:200} response and add fields back one at a time to find the offending field"],"exampleFix":"// before\nasync function handler(request) {\n  return \"ok\";\n}\n// after\nasync function handler(request) {\n  return { status: 200, headers: { \"content-type\": \"text/plain\" }, body: { raw: { data: \"ok\" } } };\n}","handlingStrategy":"validation","validationCode":"function isValidResponse(res) {\n  return res && typeof res === \"object\" && (res.status === undefined || typeof res.status === \"number\") && (res.body === undefined || typeof res.body === \"object\");\n}","typeGuard":"const isHttpResponse = (v) => !!v && typeof v === \"object\" && (\"status\" in v ? typeof v.status === \"number\" : true) && (\"body\" in v ? typeof v.body === \"object\" : true);","tryCatchPattern":"try { return await handler(request); } catch (e) { console.error(\"handler returned invalid response\", e); return { status: 500, body: { raw: { data: \"internal error\" } } }; }","preventionTips":["Always return an object matching the HttpResponse contract (status, headers, body)","Keep status numeric and body an object; never return strings/arrays from the handler","Add a unit test that runs the handler through the kernel bridge once per endpoint","Check the SiYuan changelog when upgrading for HttpResponse contract changes"],"tags":["goja","plugin-api","http-response","serialization"],"backgroundTag":"unexpected-response-shape","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}