micro/go-micro · error

changeset is nil

Error message

changeset is nil

What it means

The JSON config reader's Values method turns a source.ChangeSet into reader.Values. It first nil-checks the changeset: if ch is nil it returns "changeset is nil". This protects the reader from dereferencing a nil changeset, which would otherwise panic inside newValues. It indicates the caller passed no changeset at all, usually because an earlier source.Read produced (nil, nil) or a nil was propagated unchecked.

Source

Thrown at config/reader/json/json.go:64

	b, err := j.json.Encode(merged)
	if err != nil {
		return nil, err
	}

	cs := &source.ChangeSet{
		Timestamp: time.Now(),
		Data:      b,
		Source:    "json",
		Format:    j.json.String(),
	}
	cs.Checksum = cs.Sum()

	return cs, nil
}

func (j *jsonReader) Values(ch *source.ChangeSet) (reader.Values, error) {
	if ch == nil {
		return nil, errors.New("changeset is nil")
	}
	if ch.Format != "json" {
		return nil, errors.New("unsupported format")
	}
	return newValues(ch)
}

func (j *jsonReader) String() string {
	return "json"
}

// NewReader creates a json reader.
func NewReader(opts ...reader.Option) reader.Reader {
	options := reader.NewOptions(opts...)
	return &jsonReader{
		json: json.NewEncoder(),
		opts: options,
	}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Check the ChangeSet for nil after source.Read before calling Values.
  2. Fix any custom source whose Read returns (nil, nil); it must return a valid ChangeSet or an error.
  3. If the source genuinely has no data, return an empty ChangeSet with correct Format instead of nil.
  4. Wrap the Read->Values sequence in a helper that validates inputs once.

Example fix

// before
cs, _ := src.Read()
vals, _ := reader.Values(cs) // panics/errors if cs == nil
// after
cs, err := src.Read()
if err != nil || cs == nil {
    return fmt.Errorf("source returned no changeset: %w", err)
}
vals, err := reader.Values(cs)
Defensive patterns

Strategy: validation

Validate before calling

cs, err := src.Read()
if err != nil {
    return err
}
if cs == nil {
    return errors.New("source returned nil changeset")
}
vals, err := rdr.Values(cs)

Type guard

func validChangeSet(ch *source.ChangeSet) bool { return ch != nil && ch.Format != "" }

Prevention

When it happens

Trigger: Calling jsonReader.Values(nil) directly, or wiring a source/reader pipeline where source.Read returned a nil ChangeSet with a nil error and the result was passed to Values unchecked.

Common situations: Custom source implementations that return nil changesets on success; config code paths that skip the nil check on a Read result; tests constructing readers and passing nil to check behavior.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/e5f4514c1eb14f08. Report an issue: GitHub.