cayleygraph/cayley · error

optional iterator at the top level

Error message

optional iterator at the top level

What it means

In the MQL query session, an @optional directive is only meaningful nested inside a structure. If the top-level query shape resolves as optional, BuildIteratorTree rejects it because there is no outer context in which 'optionality' can be applied.

Source

Thrown at query/mql/build_iterator.go:51

	return shape.Save{
		From: shape.AllNodes{},
		Tags: []string{string(path)},
	}
}

func (q *Query) BuildIteratorTree(ctx context.Context, query interface{}) {
	q.isRepeated = make(map[Path]bool)
	q.queryStructure = make(map[Path]map[string]interface{})
	q.queryResult = make(map[ResultPath]map[string]interface{})
	q.queryResult[""] = make(map[string]interface{})

	var (
		opt bool
		s   shape.Shape
	)
	s, opt, q.err = q.buildShape(query, NewPath())
	if q.err == nil && opt {
		q.err = errors.New("optional iterator at the top level")
	}
	q.it = shape.BuildIterator(ctx, q.ses.qs, s)
}

func (q *Query) buildShape(query interface{}, path Path) (s shape.Shape, optional bool, err error) {
	err = nil
	optional = false
	switch t := query.(type) {
	case bool:
		// for JSON booleans
		s = shape.Lookup{quad.Bool(t)}
	case float64:
		// for JSON numbers
		// Damn you, Javascript, and your lack of integer values.
		if math.Floor(t) == t {
			// Treat it like an integer.
			s = shape.Lookup{quad.Int(t)}
		} else {

View on GitHub (pinned to 81dcd7d73e)

Solutions

  1. Remove @optional from the top-level object and keep it only on nested fields.
  2. Restructure the query so the optional clause is nested under a required parent node.
  3. If everything should be optional, select fields explicitly without the optional directive.

Example fix

// before
query := `[{"@optional": "name"}]`
// after
query := `[{"name": [], "@optional": "@optional:name"}]` // or place @optional on nested field
Defensive patterns

Strategy: validation

Validate before calling

var parsed []interface{}
json.Unmarshal([]byte(mqlQuery), &parsed)
for _, item := range parsed {
    if m, ok := item.(map[string]interface{}); ok {
        if _, has := m["@optional"]; has && len(m) <= 2 {
            return errors.New("@optional must be nested, not top-level")
        }
    }
}

Type guard

func isTopLevelOptional(q []interface{}) bool {
    for _, v := range q {
        if m, ok := v.(map[string]interface{}); ok {
            if _, has := m["@optional"]; has { return true }
        }
    }
    return false
}

Try / catch

it, err := session.Execute(ctx, mqlQuery, nil)
if err != nil && strings.Contains(err.Error(), "optional iterator at the top level") {
    return fmt.Errorf("restructure query: @optional cannot be at root")
}

Prevention

When it happens

Trigger: Running a MQL query whose root node uses @optional (or otherwise resolves to an optional shape), e.g. [{"@optional": ...}] at the top level of the query JSON.

Common situations: Copy-pasting a nested @optional clause to the top level; hand-writing MQL JSON with optional at the root when the user actually wanted all fields optional.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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