{"record":{"id":"b8f9e4291257aef9","repo":"grafana/k6","slug":"unmarshalling-options-for-sdk-w","errorCode":null,"errorMessage":"unmarshalling options for SDK: %w","messagePattern":"unmarshalling options for SDK: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/api.go","lineNumber":201,"sourceCode":"\t}\n}\n\n// StartLocalExecution starts a local-execution test run via the\n// provisioning API. It generates a K6-Idempotency-Key header for\n// safe retries. The caller provides options as pre-marshalled JSON.\nfunc (c *Client) StartLocalExecution(\n\tctx context.Context, loadTestID int64, req StartLocalExecutionRequest,\n) (*StartLocalExecutionResponse, error) {\n\t// Generate idempotency key: 8 random bytes hex-encoded (16 chars).\n\tvar key [8]byte\n\tif _, err := rand.Read(key[:]); err != nil {\n\t\treturn nil, fmt.Errorf(\"generating idempotency key: %w\", err)\n\t}\n\n\t// SDK adapter: unmarshal json.RawMessage → map[string]any.\n\tvar opts map[string]any\n\tif err := json.Unmarshal(req.Options, &opts); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshalling options for SDK: %w\", err)\n\t}\n\n\tmaxVUs, err := toInt32(req.MaxVUs)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"max_vus: %w\", err)\n\t}\n\ttotalDuration, err := toInt32(req.TotalDuration)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"total_duration: %w\", err)\n\t}\n\n\tsdkReq := k6cloud.NewStartLocalExecutionTestRequest(opts, maxVUs, totalDuration)\n\n\tif req.ArchiveSize > 0 {\n\t\tv, err := toInt32(req.ArchiveSize)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"archive_size: %w\", err)\n\t\t}","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cloudapi/provisioning/api.go#L183-L219","documentation":"Client.StartLocalExecution (internal/cloudapi/provisioning/api.go:201) adapts the caller-supplied req.Options (json.RawMessage of pre-marshalled lib.Options) into map[string]any for the generated OpenAPI SDK. This error means that payload is not valid JSON, so the request can never be built.","triggerScenarios":"StartLocalExecutionRequest.Options populated with hand-built, truncated, or empty bytes instead of the output of json.Marshal(lib.Options); upstream marshalling produced corrupt output; a test fixture contains malformed JSON.","commonSituations":"Custom code paths or tests that construct StartLocalExecutionRequest directly instead of going through cmd's marshalling of lib.Options; version skew where the Options marshalling format changed unexpectedly.","solutions":["Pass Options verbatim from json.Marshal(opts) of lib.Options","Pre-validate with json.Valid(req.Options) before calling StartLocalExecution","Log the offending payload (truncated) when the error fires to find the producer","If it comes from k6 itself rather than custom code, report a k6 bug"],"exampleFix":"// before\nreq := provisioning.StartLocalExecutionRequest{\n\tOptions: json.RawMessage(`{\"vus\":`), // truncated / invalid JSON\n}\n\n// after\nb, err := json.Marshal(libOpts)\nif err != nil {\n\treturn err\n}\nreq := provisioning.StartLocalExecutionRequest{Options: b}","handlingStrategy":"validation","validationCode":"if !json.Valid(req.Options) {\n\treturn fmt.Errorf(\"options payload is not valid JSON (%d bytes)\", len(req.Options))\n}","typeGuard":"func isValidOptionsJSON(raw json.RawMessage) bool {\n\treturn json.Valid(raw)\n}","tryCatchPattern":"if err := client.StartLocalExecution(ctx, id, req); err != nil {\n\tif strings.Contains(err.Error(), \"unmarshalling options\") {\n\t\tlog.Printf(\"options payload corrupt: %q\", truncate(req.Options, 256))\n\t}\n\treturn err\n}","preventionTips":["Always build Options with json.Marshal(lib.Options) and pass the result verbatim","Add json.Valid checks in tests that hand-construct StartLocalExecutionRequest","Never hand-concatenate JSON for this field"],"tags":["json","validation","serialization","options","provisioning"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}