mikefarah/yq · error

bad path expression, got close collect brackets without matc

Error message

bad path expression, got close collect brackets without matching opening bracket

What it means

yq compiles expressions to postfix (RPN) using a shunting-yard algorithm that tracks open/close bracket pairs. A `]` (close collect bracket) arrived while the operator stack had no matching `[`, so the expression is structurally unbalanced. The library throws this rather than guessing intent.

Source

Thrown at pkg/yqlib/expression_postfix.go:67

			opStack = append(opStack, currentToken)
			log.Debugf("put %v onto the opstack", currentToken.toString(true))
		case closeCollect, closeCollectObject:
			var opener tokenType = openCollect
			var collectOperator = collectOpType
			if currentToken.TokenType == closeCollectObject {
				opener = openCollectObject
				collectOperator = collectObjectOpType
			}

			for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
				missingClosingTokenErr := validateNoOpenTokens(opStack[len(opStack)-1])
				if missingClosingTokenErr != nil {
					return nil, missingClosingTokenErr
				}
				opStack, result = popOpToResult(opStack, result)
			}
			if len(opStack) == 0 {
				return nil, errors.New("bad path expression, got close collect brackets without matching opening bracket")
			}
			// now we should have [ as the last element on the opStack, get rid of it
			opStack = opStack[0 : len(opStack)-1]
			log.Debugf("deleting open bracket from opstack")

			//and append a collect to the result

			// hack - see if there's the optional traverse flag
			// on the close op - move it to the traverse array op
			// allows for .["cat"]?
			prefs := traversePreferences{}
			closeTokenMatch := currentToken.Match
			if closeTokenMatch[len(closeTokenMatch)-1:] == "?" {
				prefs.OptionalTraverse = true
			}
			result = append(result, &Operation{OperationType: collectOperator})
			log.Debugf("put collect onto the result")
			if opener != openCollect {

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Fix the expression so every `]` has a matching `[` (e.g. `.items[]` not `.items]`).
  2. Quote the expression in single quotes so the shell does not mangle brackets: yq '.a[0]'.
  3. If generating expressions dynamically, validate bracket balance before invoking yq.

Example fix

// before (unbalanced)
yq '.items]' file.yaml
// after
yq '.items[]' file.yaml
Defensive patterns

Strategy: validation

Validate before calling

func balanced(expr string) bool {
    var depth int
    for _, r := range expr {
        switch r {
        case '[':
            depth++
        case ']':
            depth--
            if depth < 0 {
                return false
            }
        }
    }
    return depth == 0
}

Prevention

When it happens

Trigger: Evaluating any yq expression containing `]` without a preceding `[`, e.g. `yq '.foo]'`, `yq 'select(.a)]'`, or a programmatically built expression string that lost its opening `[` (often via shell quoting stripping brackets or string interpolation).

Common situations: Shell quoting issues where `[` is treated as a glob/stripped by the shell; templating a yq expression from variables where the array-collect part is conditionally omitted; typos like `.items]` instead of `.items[]`.

Related errors


AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05). Data as JSON: /api/errors/13497b784f3f7d13. Report an issue: GitHub.