tomnomnom/gron · error
failed to form statements: %s
Error message
failed to form statements: %s
What it means
The gron action (JSON in, assignment statements out) wraps any failure from statementsFromJSON or jsonify into "failed to form statements: %s" with exit code exitFormStatements. The underlying cause (invalid JSON input, or a jsonify failure) is appended to the message.
Source
Thrown at main.go:239
// Go's maps do not have well-defined ordering, but we want a consistent
// output for a given input, so we must sort the statements
if opts&optNoSort == 0 {
sort.Sort(ss)
}
for _, s := range ss {
if opts&optJSON > 0 {
s, err = s.jsonify()
if err != nil {
goto out
}
}
fmt.Fprintln(w, conv(s))
}
out:
if err != nil {
return exitFormStatements, fmt.Errorf("failed to form statements: %s", err)
}
return exitOK, nil
}
// gronStream is like the gron action, but it treats the input as one
// JSON object per line. There's a bit of code duplication from the
// gron action, but it'd be fairly messy to combine the two actions
func gronStream(r io.Reader, w io.Writer, opts int) (int, error) {
var err error
errstr := "failed to form statements"
var i int
var sc *bufio.Scanner
var buf []byte
var conv func(s statement) string
if opts&optMonochrome > 0 {
conv = statementToString
} else {View on GitHub (pinned to 88a6234ea2)
Solutions
- Validate the input is a single well-formed JSON document (jq . < input) before piping to gron
- Use `gron --stream` (or gronStream semantics) for multiple JSON objects per line/concatenated input
- Check that the producing command (curl etc.) actually returned JSON and not an error page
- Inspect the appended cause text in the message to identify the real failure
Example fix
// before $ curl -s api | gron // api returned HTML -> failed to form statements: invalid character '<' // after $ curl -sfH 'Accept: application/json' api | jq -e . >/dev/null && curl -sfH 'Accept: application/json' api | gron
Defensive patterns
Strategy: validation
Validate before calling
// validate JSON before piping to gron
if err := json.NewDecoder(input).Decode(&v); err != nil {
// invalid JSON; don't call gron
} Type guard
func isJSON(input io.Reader) bool {
var v interface{}
dec := json.NewDecoder(input)
return dec.Decode(&v) == nil
} Try / catch
code, err := gron(input, output, opts)
if err != nil {
if strings.HasPrefix(err.Error(), "failed to form statements:") {
// log cause, check input validity
}
return err
} Prevention
- Run `jq -e .` on input before gron to prove it's valid JSON
- Use gron -s for multiple/concatenated JSON documents
- Verify upstream HTTP calls returned JSON with -f / status checks
When it happens
Trigger: `gron` receiving stdin/files that are not valid JSON; `gron --json` where jsonify fails on a malformed statement; any statementsFromJSON error such as invalid top-level JSON.
Common situations: Piping empty input, HTML error pages, YAML, or truncated JSON into gron; curling an endpoint that returned a non-200 body; feeding multiple concatenated JSON documents without --stream/-s.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- invalid JSON layout
- failed to parse '%s' as gron statement
- invalid value `%s`
- non-assignment statement
- invalid statement
AI-assisted analysis of tomnomnom/gron@88a6234ea2 (2026-09-06).
Data as JSON: /api/errors/430c701010734bb1.
Report an issue: GitHub.