{"record":{"id":"0f820b05de2e34c0","repo":"gohugoio/hugo","slug":"error-line-did-not-parse-to-a-valid-json-object-n-0f820b","errorCode":null,"errorMessage":"Error: Line did not parse to a valid JSON object\\n","messagePattern":"Error: Line did not parse to a valid JSON object\\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/warpc/genwebp/webp.c","lineNumber":339,"sourceCode":"    config->method = opts.method;\n\n    return 1;\n}\n\nInputMessage parse_input_message(const char *line)\n{\n    InputMessage msg = {0};\n\n    JSON_Value *root_value = json_parse_string(line);\n    if (root_value == NULL)\n    {\n        fprintf(stderr, \"Error parsing JSON line\\n\");\n        return msg;\n    }\n\n    if (json_value_get_type(root_value) != JSONObject)\n    {\n        fprintf(stderr, \"Error: Line did not parse to a valid JSON object\\n\");\n        json_value_free(root_value);\n        return msg;\n    }\n\n    JSON_Object *root_object = json_value_get_object(root_value);\n\n    JSON_Object *header_object = json_object_get_object(root_object, \"header\");\n    if (header_object != NULL)\n    {\n        msg.header.version = (int)json_object_get_number(header_object, \"version\");\n        msg.header.id = (int)json_object_get_number(header_object, \"id\");\n        const char *command_str = json_object_get_string(header_object, \"command\");\n        if (command_str != NULL)\n        {\n            strncpy(msg.header.command, command_str, sizeof(msg.header.command) - 1);\n            msg.header.command[sizeof(msg.header.command) - 1] = '\\0';\n        }\n        const char *err_str = json_object_get_string(header_object, \"err\");","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/internal/warpc/genwebp/webp.c#L321-L357","documentation":"Printed by genwebp's parse_input_message (webp.c:339) when json_parse_string succeeded but the root JSON value is not a JSONObject. The worker frees the value and returns an empty InputMessage with no response. Same semantics as the genavif counterpart (821).","triggerScenarios":"The host sends a JSON array, bare string/number, or any non-object root; or a framing bug concatenates objects so parson yields a non-object root.","commonSituations":"Hand-rolled test clients sending `[...]` instead of `{...}`, a protocol-version mismatch with a new batch format, leftover bytes from a prior command making the line start with a stray token, or a JSON producer emitting a top-level scalar under error.","solutions":["Send only a single JSON object per line with a `header` containing `command` and `id`.","Validate the root token type on the Go side with json.Decoder before writing.","Diff the envelope against parse_input_message and write_output_message schemas.","Add a regression test asserting a non-object root is handled deterministically."],"exampleFix":"// before\n\"decode\"\n\n// after\n{\"header\":{\"version\":1,\"id\":42,\"command\":\"decode\"},\"data\":{\"params\":{}}}","handlingStrategy":"validation","validationCode":"func mustBeJSONObject(b []byte) error {\n    dec := json.NewDecoder(bytes.NewReader(b))\n    tok, err := dec.Token()\n    if err != nil { return err }\n    if d, ok := tok.(json.Delim); !ok || d != '{' {\n        return fmt.Errorf(\"command root is not a JSON object\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build commands as a struct or map[string]any so the root is always an object.","Reject non-object roots at the host boundary.","Document the envelope schema in the warpc package and test against it."],"tags":["rpc","json","protocol","webp","validation"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}