grafana/k6 · error

unable to serialise request object: %w

Error message

unable to serialise request object: %w

What it means

After normalization, the request object is marshaled with MarshalJSON; values JSON cannot represent (functions, Symbols, BigInts) or a throwing toJSON getter make marshaling fail, wrapped as 'unable to serialise request object' (internal/js/modules/k6/grpc/client.go:393). Unlike the normalize step, this is about value types, not reference cycles.

Source

Thrown at internal/js/modules/k6/grpc/client.go:393

	if p.Timeout == time.Duration(0) {
		p.Timeout = 2 * time.Minute
	}

	if req == nil {
		return grpcReq, errors.New("request cannot be nil")
	}

	object := req.ToObject(c.vu.Runtime())

	var stack []*sobek.Object
	normalized, err := normalizeNumberStrings(object, c.vu.Runtime(), stack)
	if err != nil {
		return grpcReq, fmt.Errorf("unable to normalize number strings: %w", err)
	}

	b, err := normalized.ToObject(c.vu.Runtime()).MarshalJSON()
	if err != nil {
		return grpcReq, fmt.Errorf("unable to serialise request object: %w", err)
	}

	p.SetSystemTags(state, c.addr, method)

	return grpcext.InvokeRequest{
		Method:                 method,
		MethodDescriptor:       methodDesc,
		Timeout:                p.Timeout,
		DiscardResponseMessage: p.DiscardResponseMessage,
		Message:                b,
		TagsAndMeta:            &p.TagsAndMeta,
		Metadata:               p.Metadata,
	}, nil
}

// normalizeNumberStrings recursively traverses a sobek.Value. It creates a deep copy of any
// objects or arrays, and converts special float values (NaN, Infinity) to their
// string representations for proper JSON serialization.

View on GitHub (pinned to 93accf6570)

Solutions

  1. Convert BigInts to Number or String before invoking
  2. Strip functions and non-data fields; pass a plain data object built specifically for the message
  3. Pre-serialize once with JSON.stringify(req) in the script to get a precise early error with a stack trace

Example fix

// before
client.invoke('/svc/Set', { userId: 9007199254740993n });

// after
client.invoke('/svc/Set', { userId: '9007199254740993' }); // string keeps precision
Defensive patterns

Strategy: validation

Validate before calling

// fails on BigInt/functions with a precise JS stack trace before invoke()
function assertSerializable(req) {
  try { JSON.stringify(req); }
  catch (e) { throw new Error(`request not JSON-serializable: ${e.message}`); }
  return req;
}
client.invoke(method, assertSerializable(req));

Type guard

function isJsonSafe(v) {
  if (typeof v === 'bigint' || typeof v === 'function' || typeof v === 'symbol') return false;
  if (v === null || typeof v !== 'object') return true;
  return Object.values(v).every(isJsonSafe);
}

Try / catch

try { client.invoke(m, req); } catch (e) { if (/unable to serialise request object/.test(e.message)) { /* convert BigInts, strip functions, retry */ } throw e; }

Prevention

When it happens

Trigger: client.invoke(m, { id: 10n }) with a BigInt; passing a function or Symbol as a field; objects whose custom toJSON/getter throws during serialization.

Common situations: IDs computed as BigInt to avoid precision loss; accidentally including a function, logger, or SDK client in the payload; subclassed objects with exotic accessors.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/045175513824231b. Report an issue: GitHub.