chenhg5/cc-connect · error

slack: send preview: %w

Error message

slack: send preview: %w

What it means

SendPreviewStart posts the initial streaming-preview message via client.PostMessageContext. This error wraps any failure from that Slack Web API call (network error, invalid channel, missing scope, rate limit). The preview message could not be created, so streaming preview is aborted for this turn.

Source

Thrown at platform/slack/streaming.go:37

// SendPreviewStart posts the initial streaming-preview message (threaded like a
// normal reply) and returns a handle for subsequent edits. Implements
// core.PreviewStarter; together with UpdateMessage it lights up the engine's
// real-time streaming preview for Slack (the engine throttles the edits, so we
// stay within chat.update rate limits).
func (p *Platform) SendPreviewStart(ctx context.Context, rctx any, content string) (any, error) {
	rc, ok := rctx.(replyContext)
	if !ok {
		return nil, fmt.Errorf("slack: invalid reply context type %T", rctx)
	}
	opts := []slack.MsgOption{
		slack.MsgOptionText(core.MarkdownToSlackMrkdwn(content), false),
	}
	if rc.timestamp != "" {
		opts = append(opts, slack.MsgOptionPostMessageParameters(slack.PostMessageParameters{ThreadTimestamp: rc.timestamp}))
	}
	_, ts, err := p.client.PostMessageContext(ctx, rc.channel, opts...)
	if err != nil {
		return nil, fmt.Errorf("slack: send preview: %w", err)
	}
	return &slackPreviewHandle{channel: rc.channel, timestamp: ts}, nil
}

// UpdateMessage edits the preview message in place. The engine passes the handle
// returned by SendPreviewStart (not the reply context). Implements
// core.MessageUpdater.
func (p *Platform) UpdateMessage(ctx context.Context, previewHandle any, content string) error {
	h, ok := previewHandle.(*slackPreviewHandle)
	if !ok {
		return fmt.Errorf("slack: invalid preview handle type %T", previewHandle)
	}
	_, _, _, err := p.client.UpdateMessageContext(ctx, h.channel, h.timestamp,
		slack.MsgOptionText(core.MarkdownToSlackMrkdwn(content), false),
	)
	if err != nil {
		return fmt.Errorf("slack: update preview: %w", err)
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check the wrapped inner error: rate_limited => backoff and retry; channel_not_found / is_inactive => re-add the bot to the channel
  2. Verify the bot token has chat:write scope
  3. Confirm the channel/thread in the reply context still exists
  4. Inspect network/proxy connectivity to slack.com/api

Example fix

// before
// chat:write missing -> PostMessageContext returns invalid_auth
// after
// Slack app settings: add chat:write scope, reinstall app, restart cc-connect
Defensive patterns

Strategy: fallback

Validate before calling

// ensure bot is in channel before streaming
_, err := p.client.GetConversationInfo(&slack.GetConversationInfoInput{ChannelID: rc.channel, IncludeLocale: false})

Try / catch

handle, err := p.SendPreviewStart(ctx, rctx, content)
if err != nil {
    var rl *slack.RateLimitedError
    if errors.As(err, &rl) { /* back off and retry once */ }
    return nil // fall back to a single final reply instead of streaming
}

Prevention

When it happens

Trigger: PostMessageContext fails: channel ID invalid or bot not in channel, chat:write scope missing, Slack 429 rate limit, or ctx cancelled before the request completes.

Common situations: Bot removed from the channel mid-session; token rotated without reinstalling the app; posting to a thread whose root was deleted; hitting chat.update/chat.postMessage rate limits during heavy streaming.

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 chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/6b932da8ffda5001. Report an issue: GitHub.