projectdiscovery/katana · critical
failed to create domnormalizer
Error message
failed to create domnormalizer
What it means
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.
Source
Thrown at pkg/engine/headless/crawler/crawler.go:96
AuthUsername string
AuthPassword string
DitClassifier *dit.Classifier
// Hooks installs optional lifecycle callbacks. See Hooks for semantics.
// The zero value disables all callbacks.
Hooks Hooks
}
var domNormalizer *normalizer.Normalizer
var initOnce sync.Once
var initError error
func init() {
initOnce.Do(func() {
var err error
domNormalizer, err = normalizer.New()
if err != nil {
initError = errors.Wrap(err, "failed to create domnormalizer")
}
})
}
func New(opts Options) (*Crawler, error) {
if initError != nil {
return nil, initError
}
if opts.Logger == nil {
opts.Logger = slog.Default()
}
launcher, err := browser.NewLauncher(browser.LauncherOptions{
ChromiumPath: opts.ChromiumPath,
MaxBrowsers: opts.MaxBrowsers,
PageMaxTimeout: opts.PageMaxTimeout,
ShowBrowser: opts.ShowBrowser,View on GitHub (pinned to e3e742739c)
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
Example fix
// before
crawler, err := crawler.New(opts)
if err != nil { log.Fatal(err) } // opaque init error
// after
crawler, err := crawler.New(opts)
if err != nil {
log.Fatalf("crawler init failed: %+v", err) // %+v prints the wrapped normalizer cause
} Defensive patterns
Strategy: try-catch
Validate before calling
// smoke-test construction early in main, before starting work
if _, err := crawler.New(crawler.Options{}); err != nil {
log.Fatalf("crawler unavailable at startup: %+v", err)
} Type guard
func crawlerAvailable() bool {
_, err := crawler.New(crawler.Options{})
return err == nil
} Try / catch
cr, err := crawler.New(opts)
if err != nil {
log.Fatalf("crawler init failed: %+v", err) // %+v reveals the normalizer cause
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to create text normalizer
- captcha solver init: %w
- unsupported captcha solver provider: %s
- error compiling pattern %s: %v
- failed to apply DOM normalizer
AI-assisted analysis of projectdiscovery/katana@e3e742739c (2026-09-03).
Data as JSON: /api/errors/d570e5e2e45be77a.
Report an issue: GitHub.