mikefarah/yq · error

bad expression, got close brackets without matching opening

Error message

bad expression, got close brackets without matching opening bracket

What it means

Same shunting-yard compilation as the collect-bracket case, but for parentheses: a `)` token was reached while the operator stack contained no matching `(`. yq cannot compile the expression to postfix, so it rejects it as a bad expression.

Source

Thrown at pkg/yqlib/expression_postfix.go:107

			//traverseArrayCollect is a sneaky op that needs to be included too
			//when closing a ]
			if len(opStack) > 0 && opStack[len(opStack)-1].Operation != nil && opStack[len(opStack)-1].Operation.OperationType == traverseArrayOpType {
				opStack[len(opStack)-1].Operation.Preferences = prefs
				opStack, result = popOpToResult(opStack, result)
			}

		case closeBracket:
			for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
				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 expression, got close 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]

		default:
			var currentPrecedence = currentToken.Operation.OperationType.Precedence
			// pop off higher precedent operators onto the result
			for len(opStack) > 0 &&
				opStack[len(opStack)-1].TokenType == operationToken &&
				opStack[len(opStack)-1].Operation.OperationType.Precedence > currentPrecedence {
				opStack, result = popOpToResult(opStack, result)
			}
			// add this operator to the opStack
			opStack = append(opStack, currentToken)
			log.Debugf("put %v onto the opstack", currentToken.toString(true))
		}
	}

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Balance the parentheses in the expression so each `)` has a matching `(`.
  2. Count parens in generated/templated expressions before running them.
  3. Check the exact expression passed to yq (echo it or use --expression) to spot quoting or interpolation that removed a `(`.

Example fix

// before
yq 'select(.kind == "Deployment"))' file.yaml
// after
yq 'select(.kind == "Deployment")' file.yaml
Defensive patterns

Strategy: validation

Validate before calling

func parensBalanced(expr string) bool {
    var depth int
    for _, r := range expr {
        if r == '(' {
            depth++
        } else if r == ')' {
            depth--
            if depth < 0 {
                return false
            }
        }
    }
    return depth == 0
}

Prevention

When it happens

Trigger: Evaluating a yq expression with a stray `)`, e.g. `yq '.a)'`, `yq 'select(.x == 1))'`, or interpolation/templating that dropped the opening `(` of a function call like `select(`.

Common situations: Half-edited expressions with unmatched parens; string templates where an opening paren is missing; nested selects like `select(.a and (.b))` typo'd as `select(.a and .b))`.

Related errors


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