cayleygraph/cayley · error

expected struct, got %v

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.

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 shapes

View on GitHub (pinned to 81dcd7d73e)

Solutions

  1. 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.
  2. If the field should be a collection, annotate it appropriately rather than letting the loader recurse into it as a schema root.
  3. 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

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.