beego/beego · critical

{controllerType.String()} has no method {method}

Error message

{controllerType.String()} has no method {method}

What it means

getReflectTypeAndMethod extracted the method name from the func's runtime symbol and the controller type from the expression's single In(0) parameter, but reflect MethodByName on that controller type does not find a method with that name. The func value and its receiver type are inconsistent — something that a plain Type.Method expression can never produce, so the argument was manufactured indirectly.

Source

Thrown at server/web/router.go:621

	method = funcNameSli[lFuncSli-1]
	if len(method) == 0 {
		panic("method name is empty")
	} else if method[0] > 96 || method[0] < 65 {
		panic(fmt.Sprintf("%s is not a public method", method))
	}

	// check only one param which is the method receiver
	if numIn := funcType.NumIn(); numIn != 1 {
		panic("invalid number of param in")
	}

	controllerType = funcType.In(0)

	// check controller has the method
	_, exists := controllerType.MethodByName(method)
	if !exists {
		panic(controllerType.String() + " has no method " + method)
	}

	// check the receiver implement ControllerInterface
	if controllerType.Kind() == reflect.Ptr {
		controllerType = controllerType.Elem()
	}
	controller := reflect.New(controllerType)
	_, ok := controller.Interface().(ControllerInterface)
	if !ok {
		panic(controllerType.String() + " is not implemented ControllerInterface")
	}

	return
}

// HandleFunc define how to process the request
type HandleFunc func(ctx *beecontext.Context)

View on GitHub (pinned to 939cfde380)

Solutions

  1. Rewrite the registration as a plain method expression: web.CtrlGet("/x", MyController.Ping)
  2. go clean -cache (or rebuild) if symbols look stale after renames
  3. Make sure the method is declared directly on the controller type passed to the route, not only on an embedded struct

Example fix

// before
web.CtrlGet("/x", reflectWrapperOf(MyController)) // indirect func value

// after
web.CtrlGet("/x", MyController.Ping)
Defensive patterns

Strategy: validation

Validate before calling

func methodBelongsToController(f interface{}) bool {
    ft := reflect.TypeOf(f)
    fo := runtime.FuncForPC(reflect.ValueOf(f).Pointer())
    if fo == nil || ft.NumIn() != 1 { return false }
    name := strings.Split(fo.Name(), ".")
    _, ok := ft.In(0).MethodByName(name[len(name)-1])
    return ok
}

Type guard

// a plain method expression is always self-consistent; guard rejects indirect wrappers
func directExpr[T any](_ func(T)) {}

Try / catch

defer func() { if r := recover(); r != nil { log.Fatalf("registration: %v — pass MyController.Method directly", r) } }()

Prevention

When it happens

Trigger: Passing a method value bound through an embedded field or a wrapper created with reflect.MakeFunc that carries a method-like name; stale generated/duplicated controller code where the method expression and the receiver type come from different versions of the type after a partial rebuild.

Common situations: Weird reflection-based handler registries wrapping controller methods; incremental compile artifacts after renaming a controller method; vendored forks where a method was renamed upstream.

Related errors


AI-assisted analysis of beego/beego@939cfde380 (2026-08-15). Data as JSON: /api/errors/e4a892f873b8cf15. Report an issue: GitHub.