chenhg5/cc-connect · error

slack: invalid reply context type %T

Error message

slack: invalid reply context type %T

What it means

CreateStreamingCard implements core.StreamingCardPlatform and asserts the reply context is the Slack-internal replyContext struct. This error is returned when any other type is passed — an internal contract violation between the engine and the Slack adapter, meaning the card would be posted with an unknown destination.

Source

Thrown at platform/slack/streaming_card.go:55

	client   *slack.Client
	channel  string
	threadTS string

	mu         sync.Mutex
	ts         string // empty until the first post
	failed     bool
	lastUpdate time.Time
	lastSent   string
}

// CreateStreamingCard prepares a lazy streaming card; the Slack message is not
// posted until the first content arrives. Implements core.StreamingCardPlatform
// — when present, the engine routes the whole turn through this card and skips
// the plain streaming preview (mutually exclusive, so no double-post).
func (p *Platform) CreateStreamingCard(ctx context.Context, rctx any) (core.StreamingCard, error) {
	rc, ok := rctx.(replyContext)
	if !ok {
		return nil, fmt.Errorf("slack: invalid reply context type %T", rctx)
	}
	return &slackStreamingCard{client: p.client, channel: rc.channel, threadTS: rc.timestamp}, nil
}

// postFresh posts a brand-new message — the lazy first post for an unseen
// card, or the "too long for chat.update" overflow path used by Finalize.
// Caller must hold c.mu.
func (c *slackStreamingCard) postFresh(ctx context.Context, rendered string) (string, error) {
	opts := []slack.MsgOption{slack.MsgOptionText(rendered, false)}
	if c.threadTS != "" {
		opts = append(opts, slack.MsgOptionPostMessageParameters(slack.PostMessageParameters{ThreadTimestamp: c.threadTS}))
	}
	_, ts, err := c.client.PostMessageContext(ctx, c.channel, opts...)
	return ts, err
}

// render posts the card on first use, then edits it in place thereafter.
// Caller must hold c.mu.

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Ensure the turn's session belongs to the Slack platform (check config.toml platform binding)
  2. Recreate the session so the reply context is built by the Slack adapter
  3. Build core and platform/slack from the same source tree
  4. In tests, construct the context via p.ReconstructReplyCtx("slack:C...") or replyContext literals

Example fix

// before
card, err := p.CreateStreamingCard(ctx, "slack:C123") // string, not replyContext
// after
rc, _ := p.ReconstructReplyCtx("slack:C123")
card, err := p.CreateStreamingCard(ctx, rc)
Defensive patterns

Strategy: type-guard

Validate before calling

rc, ok := rctx.(replyContext)
if !ok { return fmt.Errorf("slack: invalid reply context type %T", rctx) }

Type guard

func isSlackReplyCtx(v any) bool { _, ok := v.(replyContext); return ok }

Try / catch

card, err := p.CreateStreamingCard(ctx, rctx)
if err != nil {
    slog.Warn("slack: streaming card unavailable, using plain preview", "err", err)
    return nil // engine falls back to plain streaming
}

Prevention

When it happens

Trigger: The engine invokes CreateStreamingCard with a reply context built by another platform, a nil context, or a test fixture of the wrong type, so the type assertion rctx.(replyContext) fails.

Common situations: Session created under one platform but resumed with the Slack platform after config edits; core/adapter version skew; unit tests passing raw structs instead of replyContext.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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