gohugoio/hugo · error

npm pack: failed to marshal package.json with updated worksp

Error message

npm pack: failed to marshal package.json with updated workspaces: %w

What it means

After appending Hugo's autogenerated workspace path to the object-form `workspaces.packages` array, Hugo re-serializes the whole package.json with json.MarshalIndent. This error wraps a marshal failure at that step. Since the input was just unmarshalled from JSON, marshal failures here are essentially never seen in practice — it's a defensive wrap around the encoder.

Source

Thrown at modules/npm/package_builder.go:292

		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)
		if err != nil {
			return fmt.Errorf("npm pack: failed to marshal package.json with updated workspaces: %w", err)
		}
		updated = append(updated, '\n')
		return afero.WriteFile(fsys, packageJSONName, updated, 0o666)
	case nil:
		// Treat explicit null as "no workspaces"; handled below as missing key.
	default:
		return fmt.Errorf("npm pack: unsupported workspaces type %T", v)
	}

	// Try adding to existing workspaces array when present.
	if hasWS && wsVal != nil {
		if wsIdx := bytes.Index(data, []byte(`"workspaces"`)); wsIdx >= 0 {
			rest := data[wsIdx:]
			if bracketOpen := bytes.IndexByte(rest, '['); bracketOpen >= 0 {
				if bracketClose := bytes.IndexByte(rest[bracketOpen:], ']'); bracketClose >= 0 {
					pos := wsIdx + bracketOpen + bracketClose
					arrayContent := bytes.TrimSpace(data[wsIdx+bracketOpen+1 : pos])
					var insertion string

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Re-run the command; if it reproduces, note this indicates an internal inconsistency.
  2. Convert `workspaces` to the simpler array form to bypass the object-form re-marshal path entirely.
  3. Report the reproducer to the Hugo issue tracker with your package.json and Hugo version.
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure workspaces contains only JSON-serializable values (strings)
for _, w := range workspaces {
    if _, ok := w.(string); !ok {
        return fmt.Errorf("workspace entry %v is not a string", w)
    }
}

Try / catch

if err := b.Build(); err != nil {
    if strings.Contains(err.Error(), "failed to marshal package.json") {
        log.Fatal("package.json contains values that cannot be re-serialized; simplify workspaces to a string array")
    }
    return err
}

Prevention

When it happens

Trigger: `hugo mod npm pack` taking the object-form workspaces path (map with `packages`), followed by json.MarshalIndent failing — which for data round-tripped through json.Unmarshal (only strings, numbers, bools, maps, slices) does not normally occur.

Common situations: Practically unheard of in normal use; would require the in-memory package map to contain unmarshalable values, pointing at a Hugo bug or memory corruption rather than user config.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/80d1ab268d4291aa.json. Report an issue: GitHub ↗.