vitessio/vitess · error

json error: %v

Error message

json error: %v

What it means

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.

Source

Thrown at go/vt/vtctl/vtctl.go:3986

		// Marshal the protobuf message.
		data, err = protojson.MarshalOptions{
			Multiline:       true,
			Indent:          "  ",
			UseProtoNames:   true,
			UseEnumNumbers:  true,
			EmitUnpopulated: true,
		}.Marshal(obj)
		if err != nil {
			return nil, fmt.Errorf("protojson error: %v", err)
		}
	case []string:
		if len(obj) == 0 {
			return []byte{'[', ']'}, nil
		}
		data, err = json.MarshalIndent(obj, "", "  ")
		if err != nil {
			return nil, fmt.Errorf("json error: %v", err)
		}
	default:
		data, err = json.MarshalIndent(obj, "", "  ")
		if err != nil {
			return nil, fmt.Errorf("json error: %v", err)
		}
	}

	return data, nil
}

// RunCommand will execute the command using the provided wrangler.
// It will return the actionPath to wait on for long remote actions if
// applicable.
func RunCommand(ctx context.Context, wr *wrangler.Wrangler, args []string) error {
	if len(args) == 0 {
		wr.Logger().Printf("No command specified. Please see the list below:\n\n")
		PrintAllCommands(wr.Logger())

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect the command result struct for fields that json cannot encode (func, chan, complex, cycles) and fix or remove them
  2. Add a MarshalJSON method to the offending custom type
  3. If the object should be empty, ensure the []byte path receives a truly empty value so the nil early-return is taken
  4. Re-run the command; if reproducible, capture the underlying %v detail which names the unsupported JSON value

Example fix

// before
type Result struct { Hook func() }
// after
type Result struct { HookName string } // encodable field instead of func
Defensive patterns

Strategy: validation

Validate before calling

if err := json.Marshal(obj); err != nil {
  return nil, fmt.Errorf("result object is not JSON-encodable: %v", err)
}

Type guard

func isJSONEncodable(v any) bool {
  return json.Valid(mustMarshalOrEmpty(v))
}

Try / catch

data, err := jsonOutput(obj)
if err != nil {
  log.Warn("vtctl output marshal failed", slog.Any("error", err))
  return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/adbb0a2c10b716ec. Report an issue: GitHub.