grafana/k6 · warning

unmarshaling hosts option

Error message

unmarshaling hosts option: %w

What it means

After marshaling the k6 hosts option to JSON, the browser module unmarshals it into map[string]string to build Chromium's --host-resolver-rules. This error means the unmarshal step produced something that is not a string-to-string map. With the current types.NullHosts representation this is a defensive, near-unreachable path; it would only fire if the JSON shape of the hosts option ever stops being a flat string map.

Solutions

  1. Check the hosts option for non-string values (numbers, objects) and make every value a plain string
  2. Remove the hosts option and re-run to isolate the trigger
  3. Align k6 and the browser module on one version (upgrade)
  4. Report upstream with the exact hosts value if it reproduces on stock k6

Example fix

// before
export const options = { hosts: { 'test.k6.io': 12345 } };

// after
export const options = { hosts: { 'test.k6.io': '1.2.3.4:80' } };
Defensive patterns

Strategy: validation

Validate before calling

// Validate that every hosts value is a string before launching
const hosts = (options.hosts ||= {});
for (const [k, v] of Object.entries(hosts)) {
  if (typeof v !== 'string') throw new Error(`hosts['${k}'] must be a string, got ${typeof v}`);
}

Prevention

When it happens

Trigger: Same launch path as [600]: browser start with the `hosts` option set, and the marshaled JSON of NullHosts not decoding into map[string]string. Realistic only across k6 versions whose hosts option JSON representation changed, or with values that are not strings.

Common situations: Version-skewed k6 installs; custom builds; effectively never on released k6 because NullHosts always encodes as {"host":"ip"}.

Related errors


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

Appendix: source

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

	// 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 (
		k6Logger = k6ext.GetVU(ctx).State().Logger
		logger   = log.New(k6Logger, common.GetIterationID(ctx))
	)

View on GitHub (pinned to 8d06114777)