plandex-ai/plandex · error

Error marshalling users: {err.Error()}

Error message

Error marshalling users: {err.Error()}

What it means

ListUsersHandler failed to serialize the ListUsersResponse (Users plus OrgUsersByUserId map) with encoding/json before writing the HTTP response. json.Marshal only errors on unsupported types (channels, funcs, cycles), so this almost always indicates a data-model defect, not a user mistake. The client receives the raw json error string appended to this message with HTTP 500.

Source

Thrown at app/server/handlers/users.go:84

		http.Error(w, "Error listing org users: "+err.Error(), http.StatusInternalServerError)
		return
	}

	orgUsersByUserId := make(map[string]*shared.OrgUser)
	for _, orgUser := range orgUsers {
		orgUsersByUserId[orgUser.UserId] = orgUser.ToApi()
	}

	resp := shared.ListUsersResponse{
		Users:            apiUsers,
		OrgUsersByUserId: orgUsersByUserId,
	}

	bytes, err := json.Marshal(resp)

	if err != nil {
		log.Println("Error marshalling users: ", err)
		http.Error(w, "Error marshalling users: "+err.Error(), http.StatusInternalServerError)
		return
	}

	log.Println("Successfully processed request for ListUsersHandler")

	w.Write(bytes)
}

func DeleteOrgUserHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Received a request for DeleteOrgUserHandler")

	if os.Getenv("GOENV") == "development" && os.Getenv("LOCAL_MODE") == "1" {
		writeApiError(w, shared.ApiError{
			Type:   shared.ApiErrorTypeOther,
			Status: http.StatusForbidden,
			Msg:    "Local mode is not supported for user management",
		})
		return

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the server log line 'Error marshalling users: ' for the exact json.UnsupportedTypeError / UnsupportedValueError and field path
  2. Remove or wrap unsupported field types (chan, func, complex) on shared.ListUsersResponse and the structs produced by ToApi()
  3. Break reference cycles in the response structs by copying values instead of pointers back to parent objects
  4. Add a unit test that marshals a fully populated ListUsersResponse fixture

Example fix

// before
type User struct {
    Callback func()
}
// after
type User struct {
    CallbackName string
}
Defensive patterns

Strategy: validation

Validate before calling

if err := json.NewEncoder(io.Discard).Encode(resp); err != nil {
    return fmt.Errorf("response not serializable: %w", err)
}

Type guard

func isMarshalable(v any) bool {
    return json.NewEncoder(io.Discard).Encode(v) == nil
}

Try / catch

bytes, err := json.Marshal(resp)
if err != nil {
    var ute *json.UnsupportedTypeError
    if errors.As(err, &ute) {
        log.Printf("unsupported type at field %s", ute.Type)
    }
    http.Error(w, "internal error", http.StatusInternalServerError)
    return
}

Prevention

When it happens

Trigger: json.Marshal(resp) returns an error — e.g. a field on a User/ToApi() struct contains a channel, func, or a reference cycle, or a custom MarshalJSON method panics/returns an error.

Common situations: Someone adds a field of type func, chan, or a cyclic pointer (e.g. *User pointing back to *Org) to the shared API structs; a custom Marshaler on ToApi() output returns an error on empty/edge-case data.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/56c1baeebedbaef2. Report an issue: GitHub.