kataras/iris · error
bindings: unresolved: not a struct type: %#+v
Error message
bindings: unresolved: not a struct type: %#+v
What it means
getBindingsForStruct builds bindings from a struct's fields, so it first asserts that the (indirected) value's kind is reflect.Struct. When it is given a non-struct value — for example a map, slice, func or basic type wrapped in reflect.Value — it panics with 'bindings: unresolved: not a struct type' to stop struct-dependent resolution early.
Source
Thrown at hero/binding.go:297
}
if !found {
missingInputs += fmt.Sprintf("\n - [%d] %s", pos, typName)
}
}
fnName := context.HandlerName(fn)
panic(fmt.Sprintf("expected [%d] bindings (input parameters) but got [%d]\nFunction:\n - %s\nExpected:%s\nMissing:%s",
expected, got, fnName, expectedInputs, missingInputs))
}
return bindings
}
func getBindingsForStruct(v reflect.Value, dependencies []*Dependency, markExportedFieldsAsRequired bool, disablePayloadAutoBinding, enableStructDependents bool, matchDependency DependencyMatcher, paramsCount int, sorter Sorter) (bindings []*binding) {
typ := indirectType(v.Type())
if typ.Kind() != reflect.Struct {
panic(fmt.Sprintf("bindings: unresolved: not a struct type: %#+v", v))
}
// get bindings from any struct's non zero values first, including unexported.
elem := reflect.Indirect(v)
nonZero := lookupNonZeroFieldValues(elem)
for _, f := range nonZero {
// fmt.Printf("Controller [%s] | NonZero | Field Index: %v | Field Type: %s\n", typ, f.Index, f.Type)
bindings = append(bindings, &binding{
Dependency: newDependency(elem.FieldByIndex(f.Index).Interface(), disablePayloadAutoBinding, enableStructDependents, nil),
Input: newStructFieldInput(f),
})
}
fields, stateless := lookupFields(elem, true, true, nil)
n := len(fields)
if n > 1 && sorter != nil {
sort.Slice(fields, func(i, j int) bool {View on GitHub (pinned to 7bedaf55a0)
Solutions
- Pass a struct or pointer-to-struct to the controller/struct registration API: container.Controller(new(MyController)) or hero.Struct(MyStruct{}).
- Check the dynamic type of the value: reflect.Indirect(reflect.TypeOf(v)).Kind() must be reflect.Struct.
- If the value is a map/config, convert it into a proper struct before registering.
- If it is an interface, assert to the concrete struct type first.
- Remove the pointer/slice wrapper if it does not point to a struct.
Example fix
// before container.Controller(myMap) // map is not a struct // after container.Controller(new(MyController)) // pointer to struct
Defensive patterns
Strategy: type-guard
Validate before calling
func assertStruct(v any) error {
if v == nil { return fmt.Errorf("nil value") }
t := reflect.Indirect(reflect.TypeOf(v))
if t.Kind() != reflect.Struct { return fmt.Errorf("%T is not a struct", v) }
return nil
}
// before registration: if err := assertStruct(ctrl); err != nil { log.Fatal(err) } Type guard
func isStructValue(v any) bool {
if v == nil { return false }
return reflect.Indirect(reflect.TypeOf(v)).Kind() == reflect.Struct
} Try / catch
func safeController(c any) (ok bool) {
defer func() {
if r := recover(); r != nil {
if s, isStr := r.(string); isStr && strings.Contains(s, "not a struct type") {
log.Printf("controller registration failed: %s", s)
} else { panic(r) }
}
}()
container.Controller(c)
return true
} Prevention
- Always register pointers to structs (new(MyController)) with controller APIs.
- Guard that dynamic types behind interfaces are structs before registration.
- Never pass maps, slices or funcs to struct-registration APIs.
- Add a startup test that constructs all controllers to surface bad types in CI.
When it happens
Trigger: Passing a non-struct value to hero.Struct / Container.Controller (which route through makeStruct -> fromStructValueOrDependentStructValue -> getBindingsForStruct), or registering a dependency whose resolved value is expected to be treated as a struct but is actually e.g. a pointer to a non-struct, a map[string]any, or a func.
Common situations: Calling container.Controller with a value instead of a struct/pointer-to-struct; registering a map-based config object as a dependency and expecting field injection; typos like &someSlice; upgrading versions where the API now requires a struct pointer; passing an interface variable whose dynamic kind is not struct.
Related errors
- bindings: unresolved: no a func type: %#+v
- expected [%d] bindings (input parameters) but got [%d] Funct
- MarkExportedFieldsAsRequired is true and at least one of str
- bad value: nil: %T
- bad value: function has zero inputs: empty input function mu
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/dee91821fa6a1554.
Report an issue: GitHub.