gohugoio/hugo · error

npm pack: unsupported workspaces object; missing "packages"…

Error message

npm pack: unsupported workspaces object; missing "packages" field

What it means

In ensureWorkspaceRef (modules/npm/package_builder.go:272). When the root package.json uses the object form of 'workspaces' ("workspaces": { ... }), Hugo requires a "packages" field inside it (the npm/yarn convention). If the object lacks "packages", the structure is unsupported and the error is returned without a wrapped cause.

Solutions

  1. Restructure the workspaces field to the standard object form: "workspaces": { "packages": ["pkg-a", "pkg-b"] }.
  2. Or use the simpler array form: "workspaces": ["pkg-a", "pkg-b"], which Hugo also supports.
  3. Ensure no extraneous keys replaced 'packages'.

Example fix

// before
"workspaces": { "apps": ["apps/*"] }

// after
"workspaces": { "packages": ["apps/*"] }
// or simply
"workspaces": ["apps/*"]
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure workspaces object form has packages
data, _ := os.ReadFile("package.json")
var pkg map[string]any
_ = json.Unmarshal(data, &pkg)
if ws, ok := pkg["workspaces"].(map[string]any); ok {
    if _, hasPkgs := ws["packages"]; !hasPkgs {
        return errors.New(`workspaces object must contain "packages"`)
    }
}

Type guard

func isSupportedWorkspacesArrayForm(v any) bool {
    switch v.(type) {
    case []any, map[string]any, nil:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: package.json contains "workspaces": { "nope": [...] } or an object form of workspaces without a packages key. This can happen with exotic monorepo tooling or hand-edited configs.

Common situations: Switching a project to/from pnpm-style workspaces without the packages key; a tool that writes a workspaces object with a different key; manually restructuring workspaces.

Related errors


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

Appendix: source

Thrown at modules/npm/package_builder.go:272

	quoted := fmt.Sprintf("%q", workspacePath)

	wsVal, hasWS := pkg["workspaces"]

	switch v := wsVal.(type) {
	case []any:
		// Array form: ["pkg-a", "pkg-b", ...]
		if slices.Contains(toStringSlice(v), workspacePath) {
			if runtime.GOOS == "windows" {
				return afero.WriteFile(fsys, packageJSONName, data, 0o666)
			}
			return nil
		}
		// Fall through to byte-based insertion into the existing array below.
	case map[string]any:
		// Object form: { "workspaces": { "packages": [...] } }
		packagesVal, ok := v["packages"]
		if !ok {
			return fmt.Errorf("npm pack: unsupported workspaces object; missing \"packages\" field")
		}
		packagesSlice := toStringSlice(packagesVal)
		if slices.Contains(packagesSlice, workspacePath) {
			if runtime.GOOS == "windows" {
				return afero.WriteFile(fsys, packageJSONName, data, 0o666)
			}
			return nil
		}

		// Append the new workspace path to the packages slice.
		newPkgs := make([]any, 0, len(packagesSlice)+1)
		for _, s := range packagesSlice {
			newPkgs = append(newPkgs, s)
		}
		newPkgs = append(newPkgs, workspacePath)
		v["packages"] = newPkgs

		updated, err := json.MarshalIndent(pkg, "", indent)

View on GitHub (pinned to 52c9bd7908)