cayleygraph/cayley · error

did not pass a string predicate or a Path to FollowRecursive

Error message

did not pass a string predicate or a Path to FollowRecursive

What it means

FollowRecursive accepts either a string predicate or a *Path describing the traversal to repeat recursively. Any other argument (nil, an unsupported wrapper type, etc.) falls into the default case of the type switch and panics, since no recursive traversal can be built from it.

Source

Thrown at query/path/path.go:396

// If 0 is passed, it will use the default value of 50 steps before returning.
// If 1 is passed, it will stop after 1 step before returning, and so on.
//
// The third argument, "depthTags" is a set of tags that will return strings of
// numeric values relating to how many applications of the path were applied the
// first time the result node was seen.
//
// This is a very expensive operation in practice. Be sure to use it wisely.
func (p *Path) FollowRecursive(via interface{}, maxDepth int, depthTags []string) *Path {
	var path *Path
	switch v := via.(type) {
	case string:
		path = StartMorphism().Out(v)
	case quad.Value:
		path = StartMorphism().Out(v)
	case *Path:
		path = v
	default:
		panic("did not pass a string predicate or a Path to FollowRecursive")
	}
	np := p.clone()
	np.stack = append(p.stack, followRecursiveMorphism(path, maxDepth, depthTags))
	return np
}

// Save will, from the current nodes in the path, retrieve the node
// one linkage away (given by either a path or a predicate), add the given
// tag, and propagate that to the result set.
//
// For example:
//  // Will return []map[string]string{{"social_status: "cool"}}
//  StartPath(qs, "B").Save("status", "social_status"
func (p *Path) Save(via interface{}, tag string) *Path {
	np := p.clone()
	np.stack = append(np.stack, saveMorphism(via, tag))
	return np
}

View on GitHub (pinned to 81dcd7d73e)

Solutions

  1. Pass the predicate name as a string, e.g. p.FollowRecursive("dependsOn", 10, nil).
  2. Pass a *Path built with StartMorphism()/StartPath, e.g. p.FollowRecursive(cayley.StartMorphism().Out("dependsOn"), 10, nil).
  3. Validate the argument is non-nil and of an accepted type before calling.

Example fix

// before
var predicate string // left nil
p.FollowRecursive(predicate, 5, nil) // panics
// after
p.FollowRecursive("dependsOn", 5, nil)
Defensive patterns

Strategy: type-guard

Validate before calling

func validFollowArg(v interface{}) bool {
    switch v.(type) {
    case string, *path.Path:
        return true
    default:
        return false
    }
}
if !validFollowArg(arg) {
    return errors.New("FollowRecursive needs a string predicate or a *Path")
}

Type guard

switch v.(type) { case string, *path.Path: return true }; return false

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("FollowRecursive failed: %v", r)
    }
}()

Prevention

When it happens

Trigger: Calling path.FollowRecursive(arg, maxDepth, depthTags) where arg is neither a string nor a *Path — typically a nil interface or a value of the wrong type.

Common situations: Passing a helper result typed interface{} instead of string/*Path; an unassigned (nil) predicate variable; passing node values from a different API layer.

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/68ad5cb49369220e. Report an issue: GitHub.