cayleygraph/cayley · error

too many close parentheses at char %d: %s

Error message

too many close parentheses at char %d: %s

What it means

The s-expression parser for query languages tracks parenthesis depth while scanning input. If a ')' appears when parenDepth is already 0, the input is malformed, so Parse returns this error with the character offset and a snippet of the input up to that point.

Source

Thrown at query/sexp/session.go:62

func NewSession(qs graph.QuadStore) *Session {
	return &Session{qs: qs}
}

func (s *Session) Parse(input string) error {
	var parenDepth int
	for i, x := range input {
		if x == '(' {
			parenDepth++
		}
		if x == ')' {
			parenDepth--
			if parenDepth < 0 {
				min := 0
				if (i - 10) > min {
					min = i - 10
				}
				return fmt.Errorf("too many close parentheses at char %d: %s", i, input[min:i])
			}
		}
	}
	if parenDepth > 0 {
		return query.ErrParseMore
	}
	if len(ParseString(input)) > 0 {
		return nil
	}
	return errors.New("invalid syntax")
}

func (s *Session) Execute(ctx context.Context, input string, opt query.Options) (query.Iterator, error) {
	switch opt.Collation {
	case query.Raw, query.REPL:
	default:
		return nil, &query.ErrUnsupportedCollation{Collation: opt.Collation}
	}

View on GitHub (pinned to 81dcd7d73e)

Solutions

  1. Count parens in the query and remove the extra ')' indicated by the char offset in the message.
  2. Parse incrementally / build queries with a helper that balances parens.
  3. If input may legitimately be incomplete, check for query.ErrParseMore semantics and feed more input instead.

Example fix

// before
q := "(start))"
// after
q := "(start)"
Defensive patterns

Strategy: validation

Validate before calling

func balanced(s string) bool {
    d := 0
    for _, r := range s {
        if r == '(' { d++ } else if r == ')' { d-- }
        if d < 0 { return false }
    }
    return d == 0
}
if !balanced(q) { /* fix input before Parse */ }

Type guard

func isBalancedSexp(s string) bool { d := 0; for _, r := range s { if r == '(' { d++ }; if r == ')' { d-- }; if d < 0 { return false } }; return d == 0 }

Try / catch

it, err := sess.Parse(q)
if err != nil {
    if errors.Is(err, query.ErrParseMore) { /* feed more input */ }
    if strings.Contains(err.Error(), "too many close parentheses") {
        // parse offset from message, repair input
    }
    return err
}

Prevention

When it happens

Trigger: Calling Parse (query/sexp/session.go) on a query string containing more ')' than '(' before it, e.g. ":a))" or an unbalanced template-generated expression.

Common situations: Hand-written s-expression queries; programmatic query builders that append extra closing parens; copy-paste errors that drop an opening paren.

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


AI-assisted analysis of cayleygraph/cayley@81dcd7d73e (2026-09-06). Data as JSON: /api/errors/17812644fde97bc4. Report an issue: GitHub.