gohugoio/hugo · critical

must have deps

Error message

must have deps

What it means

Panic raised at the top of HugoSites.Build() when h.Deps is nil. The build pipeline needs the fully-initialized dependency container (filesystems, templates, configs, loggers) before it can render anything. Calling Build() on a HugoSites that was not constructed via the normal config-load path leaves Deps nil, violating a hard precondition. This is an internal invariant guard, not a recoverable runtime condition.

Source

Thrown at hugolib/hugo_sites_build.go:91

			h.reportProgress(func() (state terminal.ProgressState, progress float64) {
				// We don't know how many files to process below, so use the intermediate state as the first progress.
				return terminal.ProgressIntermediate, 1.0
			})
		})
		defer d(func() {})
	}

	infol := h.Log.InfoCommand("build")
	defer loggers.TimeTrackf(infol, time.Now(), nil, "")
	defer func() {
		h.reportProgress(func() (state terminal.ProgressState, progress float64) {
			return terminal.ProgressHidden, 1.0
		})
		h.BuildState.BuildCounter.Add(1)
	}()

	if h.Deps == nil {
		panic("must have deps")
	}

	if !config.NoBuildLock {
		unlock, err := h.BaseFs.LockBuild()
		if err != nil {
			return fmt.Errorf("failed to acquire a build lock: %w", err)
		}
		defer unlock()
	}

	defer func() {
		for _, s := range h.Sites {
			s.Deps.BuildEndListeners.Notify()
		}
	}()

	errCollector := h.StartErrorCollector()
	errs := make(chan error)

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Construct HugoSites through hugolib.LoadHugoConfig (or NewHugoSitesFromConfig) which wires up Deps; never call Build() on a bare struct.
  2. If you reuse the object across builds, do not nil out h.Deps between builds; reuse the same dependency container or recreate the whole HugoSites.
  3. In tests, use the hugolib test helpers (e.g. hugolib.NewHugoSitesIntegrationTestBuilder or hugolib.Test) which set up Deps correctly.
  4. Add a nil-check / precondition assertion in your own wrapper before invoking Build so the failure surfaces with your own context.

Example fix

// before
h := &hugolib.HugoSites{}
h.Build(config.BuildCfg{}) // panic: must have deps

// after
h, err := hugolib.NewHugoSitesFromConfig(d, cfg)
if err != nil { return err }
h.Build(config.BuildCfg{})
Defensive patterns

Strategy: validation

Validate before calling

// Before building, ensure HugoSites was created via the standard path.
// If you hold an *hugolib.HugoSites h:
func isBuildable(h *hugolib.HugoSites) error {
    // Deps is exported on HugoSites; guard before Build.
    // (Use reflection or expose a helper if Deps is not directly comparable.)
    return nil
}
// Simplest: only ever obtain h via LoadConfig; treat any nil-Deps object as a bug.

Prevention

When it happens

Trigger: Calling (*HugoSites).Build(cfg) on a zero-value or manually-constructed HugoSites struct without going through hugolib.NewHugoSites / hugolib.LoadHugoConfig / hugolib.NewHugoSitesFromConfig. Also reachable if Depps is explicitly cleared/niled between builds or if a test instantiates HugoSites{} directly.

Common situations: Embedding Hugo as a library and skipping the initialization sequence. Test code that hand-builds a HugoSites instead of using the test helpers. A refactor that resets Deps for a rebuild without re-running LoadConfig. Memory/state corruption in long-running processes that reuse the HugoSites object across many builds.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/3186a56ce5ddf139. Report an issue: GitHub.