grafana/k6 · warning

marshaling hosts option

Error message

marshaling hosts option: %w

What it means

When the k6 browser module launches Chromium, it converts the k6 `hosts` option (types.NullHosts) into --host-resolver-rules by round-tripping it through JSON into a map[string]string. This error means json.Marshal of the hosts option failed at that conversion step. Because NullHosts marshals as a plain map[string]string, this failure is a defensive guard that is essentially unreachable on released k6 builds; seeing it usually indicates an internal type change between k6 versions or a broken custom build.

Solutions

  1. Re-run the same script with the hosts option removed to confirm the failure is hosts-specific
  2. Verify hosts entries are simple "hostname": "ip" or "hostname": "ip:port" pairs in options.hosts / K6_HOSTS
  3. Upgrade k6 to a single consistent version so lib/types and the browser module match
  4. If it reproduces on stock k6, capture K6_BROWSER_LOG=debug output and report it upstream

Example fix

// before
export const options = { hosts: { 'test.k6.io': new Date() } }; // non-string value

// after
export const options = { hosts: { 'test.k6.io': '1.2.3.4' } }; // plain host->ip mapping
Defensive patterns

Strategy: validation

Validate before calling

// In the k6 script, validate hosts entries before the run starts
export function setup() {
  const hosts = __ENV.K6_HOSTS || '';
  for (const pair of hosts.split(',')) {
    if (!pair) continue;
    const [host, ip] = pair.split('=');
    if (!host || !ip || !/^\d+\.\d+\.\d+\.\d+(:\d+)?$/.test(ip.trim())) {
      throw new Error(`Invalid hosts entry: ${pair} (expected host=ip[:port])`);
    }
  }
}

Prevention

When it happens

Trigger: Launching the browser while the k6 `hosts` option is set (options.hosts or K6_HOSTS), and the internal NullHosts value failing to serialize. Only realistic if the NullHosts type/MarshalJSON changed between mismatched k6 versions, or in a forked/custom build.

Common situations: Upgrading k6 across versions where the hosts option internal representation changed; running a custom xk6 build against an incompatible lib; practically never on stock releases.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of grafana/k6@8d06114777 (2026-08-18). Data as JSON: /api/errors/602c3912325a952c. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/chromium/browser_type.go:534

	if currHostResolver, ok := flags["host-resolver-rules"]; ok {
		hostResolver = append(hostResolver, fmt.Sprintf("%s", currHostResolver))
	}

	// Add the host resolver rules.
	//
	// This is done by marshaling the k6 hosts option to JSON and then
	// unmarshaling it to a map[string]string. This is done because the
	// k6 v0.42 changed Hosts from a map to types.NullHosts and doesn't
	// expose the map anymore.
	//
	// TODO: A better way to do this would be to handle the resolver
	// rules by communicating with Chromium (and then using Hosts's
	// Match method) instead of passing the rules via the command line
	// to Chromium.
	var rules map[string]string
	b, err := json.Marshal(k6opts.Hosts)
	if err != nil {
		return fmt.Errorf("marshaling hosts option: %w", err)
	}
	if err := json.Unmarshal(b, &rules); err != nil {
		return fmt.Errorf("unmarshaling hosts option: %w", err)
	}
	for k, v := range rules {
		hostResolver = append(hostResolver, fmt.Sprintf("MAP %s %s", k, v))
	}
	if len(hostResolver) > 0 {
		sort.Strings(hostResolver)
		flags["host-resolver-rules"] = strings.Join(hostResolver, ",")
	}

	return nil
}

// makeLogger makes and returns an extension wide logger.
func makeLogger(ctx context.Context, envLookup env.LookupFunc) (*log.Logger, error) {
	var (

View on GitHub (pinned to 8d06114777)