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

  1. Validate the input is a single well-formed JSON document (jq . < input) before piping to gron
  2. Use `gron --stream` (or gronStream semantics) for multiple JSON objects per line/concatenated input
  3. Check that the producing command (curl etc.) actually returned JSON and not an error page
  4. 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

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


AI-assisted analysis of tomnomnom/gron@88a6234ea2 (2026-09-06). Data as JSON: /api/errors/430c701010734bb1. Report an issue: GitHub.