{"record":{"id":"e4848f8b5748431f","repo":"grafana/k6","slug":"error-parsing-script-options-w","errorCode":null,"errorMessage":"error parsing script options: %w","messagePattern":"error parsing script options: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/js/bundle.go","lineNumber":205,"sourceCode":"\tvar err error\n\tch := make(chan struct{})\n\tbi.mainModule.GetExportedNames(func(names []string) {\n\t\tdefer close(ch)\n\t\tfor _, k := range names {\n\t\t\tv := bi.getExported(k)\n\t\t\tif _, ok := sobek.AssertFunction(v); ok && k != consts.Options {\n\t\t\t\tb.callableExports[k] = struct{}{}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch k {\n\t\t\tcase consts.Options:\n\t\t\t\tif !updateOptions || v == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar data []byte\n\t\t\t\tdata, err = json.MarshalIndent(v.Export(), \"\", \"  \")\n\t\t\t\tif err != nil {\n\t\t\t\t\terr = fmt.Errorf(\"error parsing script options: %w\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdec := json.NewDecoder(bytes.NewReader(data))\n\t\t\t\tdec.DisallowUnknownFields()\n\t\t\t\tif err = dec.Decode(&b.Options); err != nil {\n\t\t\t\t\tif uerr := json.Unmarshal(data, &b.Options); uerr != nil {\n\t\t\t\t\t\t// Beautify the error so we can try to show the user the key and potential value that is failing to parse\n\t\t\t\t\t\tuerr = beautifyOptionsJSONUnmarshalError(data, uerr)\n\t\t\t\t\t\terr = errext.WithAbortReasonIfNone(\n\t\t\t\t\t\t\terrext.WithExitCodeIfNone(uerr, exitcodes.InvalidConfig),\n\t\t\t\t\t\t\terrext.AbortedByScriptError,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tb.preInitState.Logger.WithError(err).Warn(\"There were unknown fields in the options exported in the script\")\n\t\t\t\t\terr = nil\n\t\t\t\t}\n\t\t\tcase consts.SetupFn:","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/bundle.go#L187-L223","documentation":"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.","triggerScenarios":"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 }.","commonSituations":"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.","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"],"exampleFix":"// before: function inside options -> json.Marshal fails\nexport const options = {\n  vus: 5,\n  duration: '30s',\n  helper: () => console.log('x'),\n};\n\n// after: only data in options; helper lives outside\nconst helper = () => console.log('x');\nexport const options = { vus: 5, duration: '30s' };","handlingStrategy":"validation","validationCode":"// self-check options before export: catches circular structures (functions are dropped silently by stringify)\ntry {\n  JSON.stringify(options);\n} catch (e) {\n  throw new Error(`exported options are not JSON-serializable: ${e.message}`);\n}\nconst hasFn = Object.values(options).some((v) => typeof v === 'function');\nif (hasFn) throw new Error('exported options must not contain functions');\nexport const options = opts;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["options","json","serialization","init-stage"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}