{"record":{"id":"058f4fd661925799","repo":"gofiber/fiber","slug":"errunknowntag","errorCode":"ErrUnknownTag","errorMessage":"logger: unknown template tag","messagePattern":"logger: unknown template tag","errorType":"panic","errorClass":"UnknownTagError","httpStatus":null,"severity":"error","filePath":"middleware/logger/logger.go","lineNumber":84,"sourceCode":"\n\t\tdataPool = sync.Pool{New: func() any { return new(Data) }}\n\t)\n\n\t// Err padding starts at the documented default and grows once on first\n\t// request to fit the longest registered route path.\n\terrPadding := defaultErrPadding\n\terrPaddingStr := strconv.Itoa(errPadding)\n\n\t// Before handling func\n\tcfg.BeforeHandlerFunc(&cfg)\n\n\t// Logger data\n\t// instead of analyzing the template inside(handler) each time, this is done once before\n\t// and we create several slices of the same length with the functions to be executed and fixed parts.\n\ttemplate, err := logtemplate.Build[fiber.Ctx, Data](cfg.Format, createTagMap(&cfg))\n\tif err != nil {\n\t\tif translated := translateBuildError(err); translated != nil {\n\t\t\tpanic(translated)\n\t\t}\n\t\tpanic(err)\n\t}\n\ttemplateChain, logFuncChain := template.Chains()\n\n\t// Return new handler\n\treturn func(c fiber.Ctx) error {\n\t\t// Don't execute middleware if Next returns true\n\t\tif cfg.Next != nil && cfg.Next(c) {\n\t\t\treturn c.Next()\n\t\t}\n\n\t\t// Set error handler once\n\t\tonce.Do(func() {\n\t\t\t// get longest possible path\n\t\t\tstack := c.App().Stack()\n\t\t\tfor m := range stack {\n\t\t\t\tfor r := range stack[m] {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/logger/logger.go#L66-L102","documentation":"logger.New compiles Config.Format into a chain of tag renderers via logtemplate.Build; if the format references a tag that has no registered renderer (e.g. ${typo} or ${reqHeader:} with an empty param), Build returns an UnknownTagError. translateBuildError converts it to the public logger.UnknownTagError (which wraps ErrUnknownTag), and logger.New panics with that typed error so the misconfiguration surfaces at startup rather than producing a silently-empty log field. The panic value supports errors.Is(err, ErrUnknownTag) and errors.As for *UnknownTagError.","triggerScenarios":"Config.Format containing ${nonexistent}, a misspelled built-in tag like ${referer} (the constant is TagReferer, no second 'r'), or a parametric tag (${query:}) with an empty/invalid parameter. Custom tags referenced before MustRegisterTag runs also hit this.","commonSituations":"Typo in the format string; referencing a context tag (e.g. ${api-key}) before the producing middleware registered it; upgrading Fiber and hitting a renamed/removed tag; copy-pasting a format from an older version.","solutions":["Correct the tag name — cross-check against the Tag* constants in middleware/logger/tags.go (e.g. ${referer}, ${status}, ${method}, ${ip}, ${latency}).","Register any custom tag with logger.MustRegisterTag BEFORE logger.New(...) compiles the format.","Recover the typed error to introspect which tag failed: use errors.As(err, &unknownTagErr) and read unknownTagErr.Tag / unknownTagErr.Hint."],"exampleFix":"// before — 'referer' misspelled\napp.Use(logger.New(logger.Config{\n    Format: \"${time} ${method} ${path} ${refererr}\\n\",\n}))\n\n// after\napp.Use(logger.New(logger.Config{\n    Format: \"${time} ${method} ${path} ${referer}\\n\",\n}))","handlingStrategy":"validation","validationCode":"// Validate the format compiles before logger.New panics at boot.\n// Easiest: boot the app in a test.\nfunc TestLoggerFormatCompiles(t *testing.T) {\n    t.Parallel()\n    assert.NotPanics(t, func() {\n        app := fiber.New()\n        app.Use(logger.New(logger.Config{Format: yourFormat}))\n        _ = app\n    })\n}","typeGuard":"func referencesKnownTags(format string, known map[string]struct{}) (missing []string) {\n    for _, m := range tagRegexp.FindAllStringSubmatch(format, -1) {\n        if _, ok := known[m[1]]; !ok {\n            missing = append(missing, m[1])\n        }\n    }\n    return\n}","tryCatchPattern":"// If you must isolate the failure (e.g. plugin host), recover the typed error:\ndefer func() {\n    if r := recover(); r != nil {\n        var ute *logger.UnknownTagError\n        if errors.As(r.(error), &ute) {\n            log.Printf(\"logger format references unknown tag %q (hint: %s)\", ute.Tag, ute.Hint)\n        }\n        panic(r) // re-panic otherwise\n    }\n}()\napp.Use(logger.New(logger.Config{Format: fmtStr}))","preventionTips":["Reference tag names from the Tag* constants, not from memory.","Register custom/context tags before logger.New compiles the format.","Add a boot smoke test in CI so a typo'd tag fails the build, not production."],"tags":["logger","template","tag-registry","config","startup-panic"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}