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

  1. Reinstall/upgrade katana so all bundled normalizer assets are present and intact
  2. Verify file permissions and that the binary runs with read access to its data files
  3. Check the wrapped cause (errors.Unwrap / %+v) to see which asset failed to load
  4. 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

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


AI-assisted analysis of projectdiscovery/katana@e3e742739c (2026-09-03). Data as JSON: /api/errors/d570e5e2e45be77a. Report an issue: GitHub.