chenhg5/cc-connect · error

slack: finalize streaming card: %w

Error message

slack: finalize streaming card: %w

What it means

Finalize renders the full final content of a streaming card. When the rendered text exceeds slackUpdateMaxText, chat.update cannot edit the existing message, so Finalize falls back to a fresh postMessage via postFresh. This error wraps a failure of that fallback post — the final content could not be delivered by either path.

Source

Thrown at platform/slack/streaming_card.go:147

	c.mu.Lock()
	defer c.mu.Unlock()
	if c.failed {
		return nil
	}
	rendered := core.MarkdownToSlackMrkdwn(content)
	if rendered == "" || rendered == c.lastSent {
		return nil
	}
	// Long reply + card already posted: chat.update would 413 with msg_too_long;
	// post a fresh message with the full content instead. This is the in-house
	// equivalent of "see full reply below" — the partial streaming card stays
	// visible above the new full-content message.
	if c.ts != "" && len(rendered) > slackUpdateMaxText {
		slog.Debug("slack: streaming card finalize switching to fresh postMessage (payload exceeds chat.update limit)",
			"size", len(rendered), "limit", slackUpdateMaxText)
		if _, err := c.postFresh(ctx, rendered); err != nil {
			c.failed = true
			return fmt.Errorf("slack: finalize streaming card: %w", err)
		}
		c.lastSent = rendered
		return nil
	}
	if err := c.render(ctx, rendered); err != nil {
		c.failed = true
		return fmt.Errorf("slack: finalize streaming card: %w", err)
	}
	c.lastSent = rendered
	return nil
}

// Failed reports whether the card has entered a terminal failed state.
func (c *slackStreamingCard) Failed() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.failed
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped inner error from postFresh (channel_not_found, rate_limited, invalid_auth) and fix accordingly
  2. Re-add the bot to the channel or restore chat:write scope if auth/membership is the cause
  3. Retry the finalize/turn delivery — the card is marked failed, so a re-send posts a fresh card
  4. Trim the response before rendering if content routinely exceeds the limit

Example fix

// before
if _, err := c.postFresh(ctx, rendered); err != nil {
    c.failed = true
    return fmt.Errorf("slack: finalize streaming card: %w", err)
}
// after
if _, err := c.postFresh(ctx, rendered); err != nil {
    c.failed = true
    slog.Error("slack: streaming card finalize fresh-post failed", "channel", c.channel, "size", len(rendered), "err", err)
    return fmt.Errorf("slack: finalize streaming card: %w", err)
}
Defensive patterns

Strategy: fallback

Validate before calling

if len(rendered) > slackUpdateMaxText {
    // oversize: verify we can still post before dropping the old preview
    if _, err := c.client.PostMessageContext(ctx, c.channel, slack.MsgOptionText("", false)); err != nil {
        return fmt.Errorf("slack: cannot post fallback: %w", err)
    }
}

Try / catch

if err := card.Finalize(ctx, fullContent); err != nil {
    // send final content as a normal message instead
    p.client.PostMessageContext(ctx, channel, slack.MsgOptionText(core.MarkdownToSlackMrkdwn(fullContent), false))
}

Prevention

When it happens

Trigger: Rendered content length > slackUpdateMaxText AND the fallback postFresh PostMessageContext call fails (channel invalid, bot removed, rate limit, network error, context cancelled).

Common situations: Very long agent responses overflowing the chat.update limit exactly when the bot lost channel access or Slack rate limiting kicked in; token scope revoked mid-session; network blip at end of a long turn.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


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