plandex-ai/plandex · error

error fetching users: %s

Error message

error fetching users: %s

What it means

This error wraps a failure from api.Client.ListUsers() when the CLI fetches the organization's user list in a background goroutine. The client returns *shared.ApiError, and its Msg field is interpolated into 'error fetching users: %s'. It signals the API call for listing users failed (network, auth, or server-side), so the revoke command aborts before it can present users for selection.

Source

Thrown at app/cli/cmd/revoke.go:43

func revoke(cmd *cobra.Command, args []string) {
	auth.MustResolveAuthWithOrg()

	email := ""
	if len(args) > 0 {
		email = args[0]
	}

	var userResp *shared.ListUsersResponse
	var pendingInvites []*shared.Invite
	errCh := make(chan error)

	term.StartSpinner("")

	go func() {
		var err *shared.ApiError
		userResp, err = api.Client.ListUsers()
		if err != nil {
			errCh <- fmt.Errorf("error fetching users: %s", err.Msg)
			return
		}
		errCh <- nil
	}()

	go func() {
		var err *shared.ApiError
		pendingInvites, err = api.Client.ListPendingInvites()
		if err != nil {
			errCh <- fmt.Errorf("error fetching pending invites: %s", err.Msg)
			return
		}
		errCh <- nil
	}()

	for i := 0; i < 2; i++ {
		err := <-errCh
		if err != nil {

View on GitHub (pinned to e2d772072e)

Solutions

  1. Run the auth/login flow again to refresh the API key or session token
  2. Verify network connectivity and proxy settings can reach the API host
  3. Confirm the CLI is pointed at the correct org and the account has member-list permissions
  4. Check the underlying err.Msg printed in the message for the exact HTTP status and act on it

Example fix

// before
errCh <- fmt.Errorf("error fetching users: %s", err.Msg)
// after
if err.Status == 401 {
	errCh <- fmt.Errorf("error fetching users: %s (run `auth login` to refresh credentials)", err.Msg)
} else {
	errCh <- fmt.Errorf("error fetching users: %s", err.Msg)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// pre-flight: ensure credentials and connectivity before invoking the command
if api.Client == nil || api.Client.APIKey == "" {
	return fmt.Errorf("not authenticated: run the login command first")
}
if err := api.Client.Ping(); err != nil {
	return fmt.Errorf("API unreachable: %w", err)
}

Type guard

func isApiError(err error) (*shared.ApiError, bool) {
	if ae, ok := err.(*shared.ApiError); ok {
		return ae, true
	}
	return nil, false
}

Try / catch

userResp, err := api.Client.ListUsers()
var apiErr *shared.ApiError
if err != nil {
	if ae, ok := err.(*shared.ApiError); ok { apiErr = ae }
	if apiErr != nil && apiErr.Status == 401 {
		return reauthAndRetry()
	}
	return fmt.Errorf("error fetching users: %w", err)
}

Prevention

When it happens

Trigger: Calling the revoke command while ListUsers() returns a non-nil *shared.ApiError: invalid or expired API key, network outage, org ID not set, or the API returning 4xx/5xx. The goroutine sends the wrapped error to errCh and the command exits.

Common situations: Expired session token after a long-lived CLI session; running the command without being logged in; corporate proxy blocking the API host; user lacking permissions to list org members.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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