cayleygraph/cayley · error
expected struct, got
Error message
expected struct, got %v
What it means
makePathForType builds a schema path for a Go type, dereferencing pointers first. If after unwrapping the type's Kind is not Struct, the schema mapping machinery cannot proceed, so it returns this error naming the offending type.
Solutions
- Ensure the destination/registered type is a struct (or pointer to struct); change var dst []Person usage to load into a struct wrapper or use a slice-aware loader path.
- If the field should be a collection, annotate it appropriately rather than letting the loader recurse into it as a schema root.
- Wrap scalar data in a struct: type Result struct { Value string `quad:"@id"` }.
Example fix
// before
people := []Person{}
c.LoadTo(ctx, qs, &people)
// after
result := struct{ People []Person `quad:"person"` }{}
c.LoadTo(ctx, qs, &result) Defensive patterns
Strategy: type-guard
Validate before calling
rt := reflect.TypeOf(dst)
for rt != nil && rt.Kind() == reflect.Ptr { rt = rt.Elem() }
if rt == nil || rt.Kind() != reflect.Struct {
return errors.New("schema destination/registration type must be a struct")
} Type guard
func isStructType(v interface{}) bool {
t := reflect.TypeOf(v)
for t != nil && t.Kind() == reflect.Ptr { t = t.Elem() }
return t != nil && t.Kind() == reflect.Struct
} Try / catch
if err := c.LoadTo(ctx, qs, dst, ids...); err != nil {
if strings.Contains(err.Error(), "expected struct, got") {
return fmt.Errorf("schema type mismatch: %w", err)
}
return err
} Prevention
- Only register/load structs (or pointers to structs) with schema.Config.
- Check embedded and tagged field types are structs where the schema recurses.
- Compile-time assertion: var _ = isStructType(Person{}) in tests.
When it happens
Trigger: Registering or loading a schema for a non-struct type: e.g. iteratorForType/makePathForType called with a slice, map, string, or interface type (or a pointer to one), or a struct field whose type is not a struct when recursion descends into it via makePathForType.
Common situations: Passing &[]Person{} (slice) as the destination; a schema tag pointing at a field of type map[string]string; registering Config.AddType with a non-struct value.
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/1b7f59e43715cc27.
Report an issue: GitHub.
Appendix: source
Thrown at schema/loader.go:176
func (c *Config) newLoader(qs graph.QuadStore) *loader {
return &loader{
c: c,
qs: qs,
pathForType: make(map[reflect.Type]*path.Path),
pathForTypeRoot: make(map[reflect.Type]*path.Path),
seen: make(map[quad.Value]reflect.Value),
}
}
func (l *loader) makePathForType(rt reflect.Type, tagPref string, rootOnly bool) (*path.Path, error) {
for rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rt.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected struct, got %v", rt)
}
if tagPref == "" {
m := l.pathForType
if rootOnly {
m = l.pathForTypeRoot
}
if p, ok := m[rt]; ok {
return p, nil
}
}
p := path.StartMorphism()
if iri := getTypeIRI(rt); iri != quad.IRI("") {
p = p.Has(l.c.iri(iriType), iri)
}
// TODO(dennwc): rewrite to shapesView on GitHub (pinned to 81dcd7d73e)