cayleygraph/cayley · error

must execute a Step

Error message

must execute a Step

What it means

linkedql.Execute takes a deserialized JSON-LD query document, converts it, and requires the result to implement the linkedql.Step interface before it can build an iterator. If the decoded item is not a Step (e.g. it is a plain document, a Document/Query wrapper, or an unknown/misspelled @type), the type assertion fails and this error is returned. It signals that the value passed to Execute is not an executable query step.

Solutions

  1. Parse the query document with linkedql.FromJSON (or the step builders) so the value is a Step before calling Execute.
  2. Verify the @type in the JSON-LD document matches a registered linkedql step name.
  3. If passing code-built queries, use constructors like linkedql.NewVertex, linkedql.Out, etc., instead of raw structs/maps.
  4. Check your linkedql/vocabulary version matches the documents you are loading (renamed or removed step types).

Example fix

// before
var doc map[string]interface{}
json.Unmarshal(data, &doc)
it, err := linkedql.Execute(ctx, qs, doc)
// after
step, err := linkedql.FromJSON(data)
if err != nil { return err }
it, err := linkedql.Execute(ctx, qs, step)
Defensive patterns

Strategy: type-guard

Validate before calling

step, err := linkedql.FromJSON(data)
if err != nil {
    return fmt.Errorf("query document is not a valid linkedql step: %w", err)
}

Type guard

if _, ok := item.(linkedql.Step); !ok {
    return fmt.Errorf("item of type %T is not a linkedql.Step; parse with linkedql.FromJSON first", item)
}

Try / catch

it, err := linkedql.Execute(ctx, qs, step)
if err != nil {
    if err.Error() == "must execute a Step" {
        return fmt.Errorf("query document @type %q is not a registered step: %w", docType, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling linkedql.Execute(ctx, qs, item) where item, after FromJSON/decoding, is not a Step — e.g. passing a raw map[string]interface{} document instead of the parsed step, a query document whose @type is not a registered step, or the output of a wrapper type rather than a step.

Common situations: Loading JSON-LD query files with an unregistered or misspelled @type; passing the top-level query envelope instead of the contained step to Execute; version drift where a step type was renamed or removed so it no longer deserializes to a Step; hand-constructing query objects without going through the step builders.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at query/linkedql/linkedql.go:56

}

// NewSession creates a new Session.
func NewSession(qs graph.QuadStore) *Session {
	return &Session{
		qs: qs,
	}
}

// Execute for a given context, query and options return an iterator of results.
func (s *Session) Execute(ctx context.Context, query string, opt query.Options) (query.Iterator, error) {
	item, err := Unmarshal([]byte(query))
	if err != nil {
		return nil, err
	}
	ns := voc.Namespaces{}
	step, ok := item.(Step)
	if !ok {
		return nil, errors.New("must execute a Step")
	}
	return BuildIterator(step, s.qs, &ns)
}

// BuildIterator for given Step returns a query.Iterator
func BuildIterator(step Step, qs graph.QuadStore, ns *voc.Namespaces) (query.Iterator, error) {
	switch s := step.(type) {
	case IteratorStep:
		return s.BuildIterator(qs, ns)
	case PathStep:
		return NewValueIteratorFromPathStep(s, qs, ns)
	}
	return nil, errors.New("must execute a IteratorStep or PathStep")
}

View on GitHub (pinned to 81dcd7d73e)