gofiber/fiber · error

sse: marshal data: %w

Error message

sse: marshal data: %w

What it means

When Event.Data is not nil/string/[]byte/json.RawMessage, Fiber JSON-encodes it (via json.Marshal or a custom JSONMarshal). This error means the value cannot be marshaled to JSON: it contains a channel, func, complex number, a custom MarshalJSON that returns an error, or a cyclic structure.

Source

Thrown at middleware/sse/event.go:109

type eventPayload struct {
	data    string
	hasData bool
}

func eventData(data any, jsonMarshal utils.JSONMarshal) (eventPayload, error) {
	switch value := data.(type) {
	case nil:
		return eventPayload{}, nil
	case string:
		return eventPayload{data: value, hasData: true}, nil
	case []byte:
		return eventPayload{data: string(value), hasData: true}, nil
	case json.RawMessage:
		return eventPayload{data: string(value), hasData: true}, nil
	default:
		encoded, err := jsonMarshal(value)
		if err != nil {
			return eventPayload{}, fmt.Errorf("sse: marshal data: %w", err)
		}
		return eventPayload{data: string(encoded), hasData: true}, nil
	}
}

func jsonMarshalOrDefault(jsonMarshal []utils.JSONMarshal) utils.JSONMarshal {
	if len(jsonMarshal) > 0 && jsonMarshal[0] != nil {
		return jsonMarshal[0]
	}
	return json.Marshal
}

func appendField(w *bytes.Buffer, field, value string) {
	w.WriteString(field) //nolint:errcheck // bytes.Buffer writes never fail.
	w.WriteString(": ")  //nolint:errcheck // bytes.Buffer writes never fail.
	w.WriteString(value) //nolint:errcheck // bytes.Buffer writes never fail.
	w.WriteByte('\n')    //nolint:errcheck // bytes.Buffer writes never fail.
}

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Only pass JSON-serializable values as Event.Data, or pass raw bytes/json.RawMessage for full control.
  2. Pre-marshal the value yourself and pass []byte to bypass Fiber's marshal step.
  3. Fix any custom MarshalJSON or app.Config.JSONEncoder that returns errors.

Example fix

// before
stream.Event(sse.Event{Data: job}) // job contains a chan/func or bad MarshalJSON

// after
raw, err := json.Marshal(job)
if err != nil { return err }
stream.Event(sse.Event{Data: json.RawMessage(raw)})
Defensive patterns

Strategy: validation

Validate before calling

// pre-marshal so errors surface before you touch the stream
raw, err := json.Marshal(payload)
if err != nil {
    return fmt.Errorf("event payload not serializable: %w", err)
}
event.Data = json.RawMessage(raw)

Type guard

// typeGuard: reject known-unserializable kinds before sending.
func jsonSerializable(v any) bool {
    if v == nil { return true }
    switch reflect.TypeOf(v).Kind() {
    case reflect.Chan, reflect.Func, reflect.UnsafePointer,
         reflect.Complex64, reflect.Complex128:
        return false
    }
    return true
}

Try / catch

if err := stream.Event(ev); err != nil {
    if strings.Contains(err.Error(), "sse: marshal data") {
        raw, mErr := json.Marshal(ev.Data)
        if mErr != nil { return mErr }
        ev.Data = json.RawMessage(raw)
        return stream.Event(ev)
    }
}

Prevention

When it happens

Trigger: Passing sse.Event{Data: make(chan int)}, a struct with a func field, a value whose MarshalJSON errors, or a custom JSONEncoder that fails; or an unmarshalable nested field.

Common situations: Accidentally putting a non-serializable Go value as event data; custom MarshalJSON that errors on edge states; a buggy custom JSONEncoder set on the app.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/2555eb3023c151a7.json. Report an issue: GitHub.