grafana/k6 · critical
error parsing script options: %w
Error message
error parsing script options: %w
What it means
After init code runs, k6 exports the script's `options` object and re-serializes it with encoding/json (json.MarshalIndent) to validate it against lib.Options. If the exported value contains something JSON cannot represent - functions, circular references, or unsupported exported Go values - the marshal fails and this error wraps the encoding/json cause. It is a script-authoring error: `options` must be a plain, JSON-serializable data object.
Source
Thrown at internal/js/bundle.go:205
var err error
ch := make(chan struct{})
bi.mainModule.GetExportedNames(func(names []string) {
defer close(ch)
for _, k := range names {
v := bi.getExported(k)
if _, ok := sobek.AssertFunction(v); ok && k != consts.Options {
b.callableExports[k] = struct{}{}
continue
}
switch k {
case consts.Options:
if !updateOptions || v == nil {
continue
}
var data []byte
data, err = json.MarshalIndent(v.Export(), "", " ")
if err != nil {
err = fmt.Errorf("error parsing script options: %w", err)
return
}
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
if err = dec.Decode(&b.Options); err != nil {
if uerr := json.Unmarshal(data, &b.Options); uerr != nil {
// Beautify the error so we can try to show the user the key and potential value that is failing to parse
uerr = beautifyOptionsJSONUnmarshalError(data, uerr)
err = errext.WithAbortReasonIfNone(
errext.WithExitCodeIfNone(uerr, exitcodes.InvalidConfig),
errext.AbortedByScriptError,
)
return
}
b.preInitState.Logger.WithError(err).Warn("There were unknown fields in the options exported in the script")
err = nil
}
case consts.SetupFn:View on GitHub (pinned to 93accf6570)
Solutions
- Strip every non-data value (functions, class instances, self-references) from the exported options object - keep only documented k6 options
- If options are computed, audit the merge sources for stray callables or cycles before export
- Move helpers and callbacks to plain module-level variables or separate exports
Example fix
// before: function inside options -> json.Marshal fails
export const options = {
vus: 5,
duration: '30s',
helper: () => console.log('x'),
};
// after: only data in options; helper lives outside
const helper = () => console.log('x');
export const options = { vus: 5, duration: '30s' }; Defensive patterns
Strategy: validation
Validate before calling
// self-check options before export: catches circular structures (functions are dropped silently by stringify)
try {
JSON.stringify(options);
} catch (e) {
throw new Error(`exported options are not JSON-serializable: ${e.message}`);
}
const hasFn = Object.values(options).some((v) => typeof v === 'function');
if (hasFn) throw new Error('exported options must not contain functions');
export const options = opts; Prevention
- Treat `options` as pure data - never store functions, class instances, or cross-references in it
- When merging option objects from multiple sources, whitelist keys instead of spreading everything
- Remember JSON.stringify silently drops functions, so also scan for callables manually
When it happens
Trigger: Exporting options that contain a function (e.g. a helper or callback accidentally merged in), a self-referencing/circular object, or a value whose Sobek export maps to a Go type json.Marshal rejects (func, chan, complex). Example: export const options = { vus: 2, helper: () => 42 }.
Common situations: Building options programmatically and merging in extra properties; copy-pasting config between files and dragging along callbacks; storing class instances or shared objects inside options.
Related errors
- unmarshalling options for SDK: %w
- invalid metric type
- invalid value type
- parsing options from script got error while parsing %q: %w
- converting argument '%v': %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/e4848f8b5748431f.
Report an issue: GitHub.