gohugoio/hugo · error
Error: Line did not parse to a valid JSON object\n
Error message
Error: Line did not parse to a valid JSON object\n
What it means
Printed by genavif's parse_input_message (avif.c:92) when json_parse_string succeeded but the top-level JSON value is not an object (e.g. a bare array, string, number, or true/false/null). The worker requires a JSON object so it can call json_object_get_object on it; anything else is treated as a malformed command, the root value is freed, and an empty InputMessage is returned with no output written.
Source
Thrown at internal/warpc/genavif/avif.c:92
} OutputMessage;
#define MAX_LINE_LENGTH 4096
InputMessage parse_input_message(const char *line)
{
InputMessage msg = {0};
JSON_Value *root_value = json_parse_string(line);
if (root_value == NULL)
{
fprintf(stderr, "Error parsing JSON line\n");
return msg;
}
if (json_value_get_type(root_value) != JSONObject)
{
fprintf(stderr, "Error: Line did not parse to a valid JSON object\n");
json_value_free(root_value);
return msg;
}
JSON_Object *root_object = json_value_get_object(root_value);
JSON_Object *header_object = json_object_get_object(root_object, "header");
if (header_object != NULL)
{
msg.header.version = (int)json_object_get_number(header_object, "version");
msg.header.id = (int)json_object_get_number(header_object, "id");
const char *command_str = json_object_get_string(header_object, "command");
if (command_str != NULL)
{
strncpy(msg.header.command, command_str, sizeof(msg.header.command) - 1);
msg.header.command[sizeof(msg.header.command) - 1] = '\0';
}
const char *err_str = json_object_get_string(header_object, "err");View on GitHub (pinned to 52c9bd7908)
Solutions
- Ensure every command line is a single JSON object with at minimum a `header` object containing `command` and `id`.
- Validate on the Go side before writing: parse the line once locally with encoding/json and reject if the root is not a json.Decoder token of type '{'.
- Add a regression test in Hugo's warpc tests that asserts a non-object root produces a defined error path rather than silence.
- Diff the message envelope against the schema documented in parse_input_message and write_output_message.
Example fix
// before: caller sent a JSON array
[{"header":{"command":"decode"}}]
// after: a single JSON object per line
{"header":{"version":1,"id":42,"command":"decode"},"data":{"params":{}}} Defensive patterns
Strategy: validation
Validate before calling
// Ensure the root JSON token is an object before sending.
func mustBeJSONObject(b []byte) error {
dec := json.NewDecoder(bytes.NewReader(b))
tok, err := dec.Token()
if err != nil { return err }
if d, ok := tok.(json.Delim); !ok || d != '{' {
return fmt.Errorf("command root is not a JSON object")
}
return nil
} Prevention
- Construct commands as map[string]any or a typed struct so the root is always an object.
- Reject non-object roots at the host boundary before they reach the worker.
- Document the envelope (header + data) explicitly in the warpc package.
When it happens
Trigger: The host (or a test harness) sends a JSON array of commands, a bare number/string, or a JSON value whose root type is not JSONObject. Also occurs if a framing bug causes two JSON objects to be concatenated on one line and parson parses only the first token in a way that yields a non-object root.
Common situations: Hand-rolled test clients that send `["decode"]` instead of `{"header":{...}}`; a protocol-version mismatch where the host uses a new array-based batch format; JSON producers that emit a top-level scalar under error conditions; or a previous command's leftover bytes making the line start with `]` or `,`.
Related errors
- Error parsing JSON line\n
- Error: Line did not parse to a valid JSON object\n
- Error reading blob header\n
- [%d] Error reading blob data (size: %llu read: %zu) \n
- Error parsing JSON line\n
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/dff5e42deaf55d76.
Report an issue: GitHub.