{"record":{"id":"d570e5e2e45be77a","repo":"projectdiscovery/katana","slug":"failed-to-create-domnormalizer","errorCode":null,"errorMessage":"failed to create domnormalizer","messagePattern":"failed to create domnormalizer","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/engine/headless/crawler/crawler.go","lineNumber":96,"sourceCode":"\tAuthUsername  string\n\tAuthPassword  string\n\tDitClassifier *dit.Classifier\n\n\t// Hooks installs optional lifecycle callbacks. See Hooks for semantics.\n\t// The zero value disables all callbacks.\n\tHooks Hooks\n}\n\nvar domNormalizer *normalizer.Normalizer\nvar initOnce sync.Once\nvar initError error\n\nfunc init() {\n\tinitOnce.Do(func() {\n\t\tvar err error\n\t\tdomNormalizer, err = normalizer.New()\n\t\tif err != nil {\n\t\t\tinitError = errors.Wrap(err, \"failed to create domnormalizer\")\n\t\t}\n\t})\n}\n\nfunc New(opts Options) (*Crawler, error) {\n\tif initError != nil {\n\t\treturn nil, initError\n\t}\n\n\tif opts.Logger == nil {\n\t\topts.Logger = slog.Default()\n\t}\n\n\tlauncher, err := browser.NewLauncher(browser.LauncherOptions{\n\t\tChromiumPath:        opts.ChromiumPath,\n\t\tMaxBrowsers:         opts.MaxBrowsers,\n\t\tPageMaxTimeout:      opts.PageMaxTimeout,\n\t\tShowBrowser:         opts.ShowBrowser,","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/projectdiscovery/katana/blob/e3e742739c3746f085943ce918fb4e2b8daf6fe6/pkg/engine/headless/crawler/crawler.go#L78-L114","documentation":"Package-level init error in katana's headless crawler package: the sync.Once initializer failed to construct the shared DOM normalizer (normalizer.New). The wrapped cause comes from normalizer.New -> NewTextNormalizer, which typically loads normalization assets (e.g. dictionary/stopword data). New(opts Options) then returns this stored initError for every crawler construction, so all crawler creation fails.","triggerScenarios":"1) NewTextNormalizer cannot load its required resource files (missing/corrupt embedded or on-disk data). 2) Build/packaging stripped required assets (custom builds, missing go:embed outputs). 3) Filesystem permission or read errors when assets are loaded from disk.","commonSituations":"Custom Docker images with trimmed files; running from an unexpected working directory where data files can't be found; corrupted install of katana; regression after upgrading katana versions.","solutions":["Reinstall/upgrade katana so all bundled normalizer assets are present and intact","Verify file permissions and that the binary runs with read access to its data files","Check the wrapped cause (errors.Unwrap / %+v) to see which asset failed to load","If vendoring/forking, confirm go:embed resources are included in your build"],"exampleFix":"// before\ncrawler, err := crawler.New(opts)\nif err != nil { log.Fatal(err) } // opaque init error\n// after\ncrawler, err := crawler.New(opts)\nif err != nil {\n    log.Fatalf(\"crawler init failed: %+v\", err) // %+v prints the wrapped normalizer cause\n}","handlingStrategy":"try-catch","validationCode":"// smoke-test construction early in main, before starting work\nif _, err := crawler.New(crawler.Options{}); err != nil {\n    log.Fatalf(\"crawler unavailable at startup: %+v\", err)\n}","typeGuard":"func crawlerAvailable() bool {\n    _, err := crawler.New(crawler.Options{})\n    return err == nil\n}","tryCatchPattern":"cr, err := crawler.New(opts)\nif err != nil {\n    log.Fatalf(\"crawler init failed: %+v\", err) // %+v reveals the normalizer cause\n}","preventionTips":["Verify normalizer assets ship in your build/container","Fail fast at startup, not per-request","Print errors with %+v to see wrapped causes","Reinstall katana after failed upgrades"],"tags":["initialization","normalizer","katana"],"backgroundTag":"normalizer-init-failed","analyzedSha":"e3e742739c3746f085943ce918fb4e2b8daf6fe6","analyzedAt":"2026-09-03T14:55:13.248Z","contentChangedAt":"2026-09-03T14:55:13.248Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}