{"id":"c7a7d5f231e9cea2","repo":"labstack/echo","slug":"panic-err-from-config-tomiddleware-in-requestlo","errorCode":null,"errorMessage":"panic: err from config.ToMiddleware() in RequestLoggerWithConfig","messagePattern":"panic: err from config\\.ToMiddleware\\(\\) in RequestLoggerWithConfig","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/request_logger.go","lineNumber":240,"sourceCode":"\tResponseSize int64\n\t// Headers are list of headers from request. Note: request can contain more than one header with same value so slice\n\t// of values is what will be returned/logged for each given header.\n\t// Note: header values are converted to canonical form with http.CanonicalHeaderKey as this how request parser converts header\n\t// names to. For example, the canonical key for \"accept-encoding\" is \"Accept-Encoding\".\n\tHeaders map[string][]string\n\t// QueryParams are list of query parameters from request URI. Note: request can contain more than one query parameter\n\t// with same name so slice of values is what will be returned/logged for each given query param name.\n\tQueryParams map[string][]string\n\t// FormValues are list of form values from request body+URI. Note: request can contain more than one form value with\n\t// same name so slice of values is what will be returned/logged for each given form value name.\n\tFormValues map[string][]string\n}\n\n// RequestLoggerWithConfig returns a RequestLogger middleware with config.\nfunc RequestLoggerWithConfig(config RequestLoggerConfig) echo.MiddlewareFunc {\n\tmw, err := config.ToMiddleware()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn mw\n}\n\n// ToMiddleware converts RequestLoggerConfig into middleware or returns an error for invalid configuration.\nfunc (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {\n\tif config.Skipper == nil {\n\t\tconfig.Skipper = DefaultSkipper\n\t}\n\tnow := time.Now\n\tif config.timeNow != nil {\n\t\tnow = config.timeNow\n\t}\n\n\tif config.LogValuesFunc == nil {\n\t\treturn nil, errors.New(\"missing LogValuesFunc callback function for request logger middleware\")\n\t}\n","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/labstack/echo/blob/05489dc1730161df26b72d1ae2a3ba6fb8178fc7/middleware/request_logger.go#L222-L258","documentation":"This panic is raised by RequestLoggerWithConfig when config.ToMiddleware() returns a non-nil error. The only validation ToMiddleware performs is at request_logger.go:255-257: it requires config.LogValuesFunc to be non-nil (the field doc at line 131 explicitly marks it 'Mandatory'). Because RequestLoggerWithConfig has no way to surface the error to the caller, it panics with the underlying error value rather than propagating it through the Echo middleware registration path.","triggerScenarios":"Calling middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{...}) with a struct literal that omits the LogValuesFunc field, or sets it to nil. This commonly happens when a developer copies a Logger() config (which needs no callback) and forgets that the RequestLogger variant is callback-driven, or when LogValuesFunc is conditionally assigned (e.g. behind an env flag) and the branch that leaves it unset executes at startup.","commonSituations":"Migrating from the older Logger()/LoggerWithConfig middleware to the newer RequestLogger API and assuming it has sane defaults; refactoring that accidentally drops the LogValuesFunc assignment; wiring the logger conditionally (dev vs prod) where one path leaves it nil; initializing the config in one place and assigning the callback in another that hasn't run yet by the time e.Use() is called.","solutions":["Set the LogValuesFunc field in your RequestLoggerConfig — it is mandatory and must be a non-nil func(c *echo.Context, v middleware.RequestLoggerValues) error.","If you want default behavior with no custom sink, use the zero-argument middleware.RequestLogger() constructor (request_logger.go:395) which wires its own LogValuesFunc to c.Logger() for you.","If configuration is built dynamically or from external input, call config.ToMiddleware() directly so you receive the error instead of panicking, then handle or log it before registering the middleware.","Add a nil check before e.Use(): if cfg.LogValuesFunc == nil { return fmt.Errorf(\"LogValuesFunc is required\") }."],"exampleFix":"// before\ne.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{\n    LogStatus: true,\n    LogURI:    true,\n}))\n\n// after\ne.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{\n    LogStatus: true,\n    LogURI:    true,\n    LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error {\n        fmt.Printf(\"uri=%s status=%d\\n\", v.URI, v.Status)\n        return nil\n    },\n}))","handlingStrategy":"validation","validationCode":"// Validate before registering the middleware\nfunc buildRequestLogger(cfg middleware.RequestLoggerConfig) (echo.MiddlewareFunc, error) {\n    if cfg.LogValuesFunc == nil {\n        return nil, errors.New(\"RequestLoggerConfig.LogValuesFunc is mandatory\")\n    }\n    return cfg.ToMiddleware() // returns (mw, err) instead of panicking\n}\n\n// usage\nmw, err := buildRequestLogger(cfg)\nif err != nil {\n    log.Fatal(err)\n}\ne.Use(mw)","typeGuard":"// Compile-time guarantee that the config carries a non-nil callback.\n// (Go has no structural type guard; use a constructor that enforces it.)\nfunc NewRequestLogger(logValuesFunc func(c *echo.Context, v middleware.RequestLoggerValues) error, opts ...func(*middleware.RequestLoggerConfig)) echo.MiddlewareFunc {\n    if logValuesFunc == nil {\n        panic(\"NewRequestLogger: logValuesFunc must not be nil\")\n    }\n    cfg := middleware.RequestLoggerConfig{LogValuesFunc: logValuesFunc}\n    for _, opt := range opts {\n        opt(&cfg)\n    }\n    mw, err := cfg.ToMiddleware()\n    if err != nil {\n        panic(err)\n    }\n    return mw\n}","tryCatchPattern":"// Go has no try/catch; recover in main() only as a last-resort guard during\n// application bootstrap. Prefer validation. If you must isolate panics during\n// wiring, wrap e.Use calls:\nfunc safeUse(e *echo.Echo, name string, build func() echo.MiddlewareFunc) {\n    defer func() {\n        if r := recover(); r != nil {\n            log.Fatalf(\"failed to register %s: %v\", name, r)\n        }\n    }()\n    e.Use(build())\n}\n\nsafeUse(e, \"request-logger\", func() echo.MiddlewareFunc {\n    return middleware.RequestLoggerWithConfig(cfg)\n})","preventionTips":["Treat LogValuesFunc as a required constructor argument: write a small wrapper/constructor that accepts the callback as its first parameter so it can never be omitted.","Prefer config.ToMiddleware() over RequestLoggerWithConfig when the config is built from dynamic or external sources — it returns an error instead of panicking.","For default logging with no custom sink, use middleware.RequestLogger() instead of building a RequestLoggerConfig yourself.","Add a startup unit test that exercises e.Use(...) on a freshly built Echo instance to fail-fast in CI rather than at runtime.","Keep the callback assignment co-located with the RequestLoggerConfig literal rather than assigning it later in a separate code path."],"tags":["middleware","request-logger","configuration","panic","startup","echo"],"analyzedSha":"05489dc1730161df26b72d1ae2a3ba6fb8178fc7","analyzedAt":"2026-08-04T21:32:47.783Z","schemaVersion":2}