chenhg5/cc-connect · error

tuitui: platform stopped

Error message

tuitui: platform stopped

What it means

Start() refuses to (re)start the platform once p.stopping has been set, i.e. after Stop() was called or shutdown began. It also registers the message handler and launches the connect loop only on a successful start. This guards against reusing a Platform instance that is being torn down.

Source

Thrown at platform/tuitui/tuitui.go:173

		allowFrom:             allowFrom,
		groupAllowFrom:        groupAllowFrom,
		ignoreFrom:            ignoreFrom,
		groupPolicy:           groupPolicy,
		receiveReaction:       receiveReaction,
		requireMention:        requireMention,
		shareSessionInChannel: shareSessionInChannel,
		pendingHistoryLimit:   pendingHistoryLimit,
		client:                &http.Client{Timeout: httpTimeout},
	}, nil
}

func (p *Platform) Name() string { return "tuitui" }

func (p *Platform) Start(handler core.MessageHandler) error {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.stopping {
		return fmt.Errorf("tuitui: platform stopped")
	}
	p.handler = handler
	ctx, cancel := context.WithCancel(context.Background())
	p.cancel = cancel
	go p.connectLoop(ctx)
	return nil
}

func (p *Platform) Stop() error {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.stopping = true
	if p.cancel != nil {
		p.cancel()
	}
	return nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Create a fresh Platform instance via tuitui.New and Start that instead of restarting the stopped one.
  2. Ensure Stop() is only called at final teardown; order your lifecycle so Start always precedes Stop.
  3. Synchronize Start/Stop calls (mutex or single lifecycle owner) to avoid racing with stopping.

Example fix

// before
p.Stop()
err := p.Start(handler) // fails: platform stopped
// after
p.Stop()
p2, err := tuitui.New(opts)
if err == nil {
    err = p2.Start(handler)
}
Defensive patterns

Strategy: fallback

Validate before calling

// track lifecycle at call site
if platformStopped {
    p, err = tuitui.New(opts)
}

Type guard

func (p *tuitui.Platform) isStopped() bool { return p.Stopped() } // if exposed

Try / catch

if err := p.Start(handler); err != nil && strings.Contains(err.Error(), "platform stopped") {
    p = mustNewPlatform(opts)
    err = p.Start(handler)
}

Prevention

When it happens

Trigger: Calling Start(handler) on a *Platform whose Stop() was already invoked, or calling Start concurrently with shutdown; typically a restart attempt on the same instance after engine teardown.

Common situations: Config reload code that stops then tries to Start the same Platform object instead of constructing a new one; a reconnect loop at the application level calling Start after a graceful shutdown; race between Stop and Start during app exit.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/d4547206618f909a. Report an issue: GitHub.