tomnomnom/gron · error
non-assignment statement
Error message
non-assignment statement
What it means
statement.jsonify() converts a gron assignment statement into a JSON-equivalent form. It only accepts statements that look like a complete assignment: at least 4 tokens, starting with a bare word, with '=' as the third-to-last token and ';' as the last. If the token slice doesn't match this shape, jsonify returns "non-assignment statement".
Source
Thrown at statements.go:79
return append(
new,
token{".", typDot},
token{k, typBare},
)
}
// jsonify converts an assignment statement to a JSON representation
func (s statement) jsonify() (statement, error) {
// If m is the number of keys occurring in the left hand side
// of s, then len(s) is in between 2*m+4 and 3*m+4. The resultant
// statement j (carrying the JSON representation) is always 2*m+5
// long. So len(s)+1 ≥ 2*m+5 = len(j). Therefore an initaial
// allocation of j with capacity len(s)+1 will allow us to carry
// through without reallocation.
j := make(statement, 0, len(s)+1)
if len(s) < 4 || s[0].typ != typBare || s[len(s)-3].typ != typEquals ||
s[len(s)-1].typ != typSemi {
return nil, errors.New("non-assignment statement")
}
j = append(j, token{"[", typLBrace})
j = append(j, token{"[", typLBrace})
for _, t := range s[1 : len(s)-3] {
switch t.typ {
case typNumericKey, typQuotedKey:
j = append(j, t)
j = append(j, token{",", typComma})
case typBare:
j = append(j, token{quoteString(t.text), typQuotedKey})
j = append(j, token{",", typComma})
}
}
if j[len(j)-1].typ == typComma {
j = j[:len(j)-1]
}
j = append(j, token{"]", typLBrace})View on GitHub (pinned to 88a6234ea2)
Solutions
- Ensure the statement ends with '=' followed by a value and a ';' before calling jsonify
- Only call jsonify on statements produced by statementsFromJSON / the gron pipeline, not on hand-assembled token fragments
- Check statement length and token types (s[0].typ == typBare, s[len-3].typ == typEquals, s[len-1].typ == typSemi) before calling jsonify
Example fix
// before
s := statement{{"json", typBare}}
j, err := s.jsonify() // non-assignment statement
// after
s := statement{{"json", typBare}, {"=", typEquals}, {"null", typNull}, {";", typSemi}}
j, err := s.jsonify() // ok Defensive patterns
Strategy: validation
Validate before calling
func isAssignment(s statement) bool {
return len(s) >= 4 && s[0].typ == typBare &&
s[len(s)-3].typ == typEquals && s[len(s)-1].typ == typSemi
}
if !isAssignment(s) { /* skip or fix before calling jsonify */ } Type guard
func isAssignment(s statement) bool {
return len(s) >= 4 && s[0].typ == typBare &&
s[len(s)-3].typ == typEquals && s[len(s)-1].typ == typSemi
} Try / catch
j, err := s.jsonify()
if err != nil {
if err.Error() == "non-assignment statement" {
// skip fragment / log and continue
}
return err
} Prevention
- Only jsonify complete `key = value;` statements
- Assert statement shape (bare head, '=', value, ';') before conversion
- Avoid jsonify on partial/intermediate token fragments
When it happens
Trigger: Calling jsonify() on a statement built from tokens that are not a full `key = value;` assignment — e.g. a bare-key fragment with no '=' terminator, an empty or truncated statement, or a prefix statement missing the trailing semicolon.
Common situations: Piping `gron --json` output back through gron's JSON-formatted path, or programmatically constructing statement tokens and calling jsonify on partial statements (e.g. key-only fragments produced while building output incrementally).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- statement has no value
- no statements were parsed
- invalid JSON layout
- invalid statement
- failed to form statements: %s
AI-assisted analysis of tomnomnom/gron@88a6234ea2 (2026-09-06).
Data as JSON: /api/errors/5dd7d29f0e749faf.
Report an issue: GitHub.