{"record":{"id":"adbb0a2c10b716ec","repo":"vitessio/vitess","slug":"json-error-v","errorCode":null,"errorMessage":"json error: %v","messagePattern":"json error: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/vtctl.go","lineNumber":3986,"sourceCode":"\n\t\t// Marshal the protobuf message.\n\t\tdata, err = protojson.MarshalOptions{\n\t\t\tMultiline:       true,\n\t\t\tIndent:          \"  \",\n\t\t\tUseProtoNames:   true,\n\t\t\tUseEnumNumbers:  true,\n\t\t\tEmitUnpopulated: true,\n\t\t}.Marshal(obj)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"protojson error: %v\", err)\n\t\t}\n\tcase []string:\n\t\tif len(obj) == 0 {\n\t\t\treturn []byte{'[', ']'}, nil\n\t\t}\n\t\tdata, err = json.MarshalIndent(obj, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"json error: %v\", err)\n\t\t}\n\tdefault:\n\t\tdata, err = json.MarshalIndent(obj, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"json error: %v\", err)\n\t\t}\n\t}\n\n\treturn data, nil\n}\n\n// RunCommand will execute the command using the provided wrangler.\n// It will return the actionPath to wait on for long remote actions if\n// applicable.\nfunc RunCommand(ctx context.Context, wr *wrangler.Wrangler, args []string) error {\n\tif len(args) == 0 {\n\t\twr.Logger().Printf(\"No command specified. Please see the list below:\\n\\n\")\n\t\tPrintAllCommands(wr.Logger())","sourceCodeStart":3968,"sourceCodeEnd":4004,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/vtctl.go#L3968-L4004","documentation":"vtctl's JSON output helper wraps any failure of json.MarshalIndent when serializing a command's result object. The error means Go's encoding/json could not serialize the value (e.g. an unsupported type such as a channel, func, or complex number, or a cyclic structure). It is thrown so the caller returns a descriptive error instead of an unusable nil data blob.","triggerScenarios":"Calling a vtctl command whose result object is marshaled in the 'case []byte' branch path: len(obj)==0 returns nil early, otherwise json.MarshalIndent(obj, \"\", \"  \") fails on an unencodable value.","commonSituations":"A command result contains a field of an unsupported type (func, chan, complex) or a reference cycle; custom types without MarshalJSON used in vtctl command output; regressions after changing a result struct.","solutions":["Inspect the command result struct for fields that json cannot encode (func, chan, complex, cycles) and fix or remove them","Add a MarshalJSON method to the offending custom type","If the object should be empty, ensure the []byte path receives a truly empty value so the nil early-return is taken","Re-run the command; if reproducible, capture the underlying %v detail which names the unsupported JSON value"],"exampleFix":"// before\ntype Result struct { Hook func() }\n// after\ntype Result struct { HookName string } // encodable field instead of func","handlingStrategy":"validation","validationCode":"if err := json.Marshal(obj); err != nil {\n  return nil, fmt.Errorf(\"result object is not JSON-encodable: %v\", err)\n}","typeGuard":"func isJSONEncodable(v any) bool {\n  return json.Valid(mustMarshalOrEmpty(v))\n}","tryCatchPattern":"data, err := jsonOutput(obj)\nif err != nil {\n  log.Warn(\"vtctl output marshal failed\", slog.Any(\"error\", err))\n  return err\n}","preventionTips":["Keep vtctl command result structs free of func/chan/complex fields","Add json.Marshaler implementations for custom output types","Add unit tests that marshal every command's zero-value result"],"tags":["go","json","serialization","vtctl"],"backgroundTag":"json-marshaling-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}