{"record":{"id":"c15e9ce5b922907d","repo":"siyuan-note/siyuan","slug":"handler-did-not-return-an-object","errorCode":null,"errorMessage":"handler did not return an object","messagePattern":"handler did not return an object","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/plugin.go","lineNumber":928,"sourceCode":"\t\t\terr = getHandlerErr\n\t\t\treturn\n\t\t}\n\n\t\tjsRequest, convertErr := requestGoToJs(p, rt, request)\n\t\tif convertErr != nil {\n\t\t\terr = convertErr\n\t\t\treturn\n\t\t}\n\n\t\tinvokeFunction(func(rt *goja.Runtime, result *CallResult) {\n\t\t\tif result.Error != nil {\n\t\t\t\tdone <- &handleResult{Error: result.Error}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tresponseObj := result.Value.ToObject(rt)\n\t\t\tif responseObj == nil {\n\t\t\t\tdone <- &handleResult{Error: fmt.Errorf(\"handler did not return an object\")}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// convert response.body?.raw?.data from (string | Buffer | ArrayBuffer) to []byte\n\t\t\tvar raw *[]byte\n\t\t\tif bodyValue := responseObj.Get(\"body\"); isJsValueNotNull(bodyValue) {\n\t\t\t\t// response.body\n\t\t\t\tif bodyObj := bodyValue.ToObject(rt); bodyObj != nil {\n\t\t\t\t\tif rawValue := bodyObj.Get(\"raw\"); isJsValueNotNull(rawValue) {\n\t\t\t\t\t\t// response.body.raw\n\t\t\t\t\t\tif rawObj := rawValue.ToObject(rt); rawObj != nil {\n\t\t\t\t\t\t\tif dataValue := rawObj.Get(\"data\"); isJsValueNotNull(dataValue) {\n\t\t\t\t\t\t\t\t// response.body.raw.data\n\t\t\t\t\t\t\t\tdataBytes, convertErr := jsValueToBytes(rt, dataValue)\n\t\t\t\t\t\t\t\tif convertErr == nil {\n\t\t\t\t\t\t\t\t\traw = &dataBytes\n\t\t\t\t\t\t\t\t\trawObj.Set(\"data\", goja.Null())\n\t\t\t\t\t\t\t\t}","sourceCodeStart":910,"sourceCodeEnd":946,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/plugin.go#L910-L946","documentation":"In handleHttpRequest, after the JS handler returns, the kernel converts result.Value to an object; if ToObject yields nil (the handler returned undefined/null or a primitive that cannot become an object in a meaningful way), the kernel reports `handler did not return an object` as the handleResult error. HTTP handling expects a response-shaped object with fields like status/body.","triggerScenarios":"A plugin HTTP-request handler that returns nothing (falls off the end), returns undefined/null explicitly, or returns a Promise resolved to undefined instead of a response object.","commonSituations":"Handler with early-return paths that skip building the response; awaiting a helper that returns undefined on error; plugin returning the result of a void function instead of a `{status, body}` object.","solutions":["Make the handler always return a response object on every code path, e.g. `{ status: 200, body: ... }`","Ensure the Promise passed to invokeFunction resolves to the response, not to a side-effect-only call","Add a JS-side guard: if the computed response is falsy, return a default 500 response object","Check early-return branches in the handler for missing return values"],"exampleFix":"// before\nasync handle(req) {\n  if (!req.path) return            // falls through -> undefined\n  return { status: 200, body: 'ok' }\n}\n// after\nasync handle(req) {\n  if (!req.path) return { status: 400, body: 'missing path' }\n  return { status: 200, body: 'ok' }\n}","handlingStrategy":"validation","validationCode":"// plugin JS: validate the response before returning it\nfunction makeResponse(status, body) {\n  if (typeof status !== 'number') status = 500\n  if (body === undefined || body === null) body = ''\n  return { status: status, body: body }\n}","typeGuard":"// plugin JS\nfunction isResponseObject(v) {\n  return v !== null && typeof v === 'object' && typeof v.status === 'number'\n}","tryCatchPattern":"// kernel caller side\nres := p.handleHttpRequest(req)\nif res.Error != nil && res.Error.Error() == \"handler did not return an object\" {\n  http.Error(w, \"plugin handler returned no response\", http.StatusBadGateway)\n}","preventionTips":["Always return an explicit response object from every branch of the handler","Wrap handler bodies so the final result is `{status, body}` even on error","Never return the result of a void helper; build the response object explicitly","Unit-test handlers with early-return inputs (missing fields, bad methods)"],"tags":["plugin","http","goja","unexpected-response"],"backgroundTag":"unexpected-response-shape","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}