beego/beego · critical

{controllerType.String()} is not implemented ControllerInter

Error message

{controllerType.String()} is not implemented ControllerInterface

What it means

The receiver type resolved from the method expression must implement web.ControllerInterface — in practice every beego controller gets this by embedding web.Controller. getReflectTypeAndMethod instantiates the type with reflect.New and type-asserts to ControllerInterface; on failure it panics with '<type> is not implemented ControllerInterface' at registration time.

Source

Thrown at server/web/router.go:631

		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)

// Get add get method
// usage:
//
//	Get("/", func(ctx *context.Context){
//	      ctx.Output.Body("hello world")
//	})
func (p *ControllerRegister) Get(pattern string, f HandleFunc) {
	p.AddMethod("get", pattern, f)
}

View on GitHub (pinned to 939cfde380)

Solutions

  1. Embed web.Controller in the struct: type Api struct { web.Controller }
  2. If you cannot embed it, use beego.Get(pattern, func(ctx *context.Context)) instead of the Ctrl* method-expression API
  3. Re-run after embedding to confirm the registration panic is gone

Example fix

// before
type Api struct{}
func (a Api) Ping() {}
web.CtrlGet("/ping", Api.Ping)

// after
type Api struct{ web.Controller }
func (a Api) Ping() { a.Ctx.Output.Body([]byte("pong")) }
web.CtrlGet("/ping", Api.Ping)
Defensive patterns

Strategy: type-guard

Validate before calling

var _ web.ControllerInterface = (*MyController)(nil) // compile-time check at package level

Type guard

type Controller interface{ web.ControllerInterface }

func RegisterCtrl[T Controller](pattern string, m func(T)) {
    // T guaranteed to implement ControllerInterface at compile time
}
RegisterCtrl("/ping", MyController.Ping) // fails to compile if MyController lacks web.Controller

Try / catch

defer func() { if r := recover(); r != nil { log.Fatalf("controller must embed web.Controller: %v", r) } }()

Prevention

When it happens

Trigger: web.CtrlGet("/x", Api.Ping) where type Api struct{} does not embed web.Controller (and does not implement Init/Prepare/... itself); using a plain struct with exported parameterless methods in the Ctrl* API.

Common situations: Reusing a service/domain struct as a 'controller'; writing a minimal handler struct for beego v2 without embedding the Controller; partially migrating controllers that previously satisfied an older interface.

Related errors


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