chenhg5/cc-connect · error

tuitui: team_id not found for channel %q

Error message

tuitui: team_id not found for channel %q

What it means

For channel-type IDs, SendChannelPost must translate the channel into a teams chat ID, which requires the owning team's ID. It fetches channel info via getChannelInfo and, if the returned map has no non-empty "team_id", fails with this error naming the channel. The channel exists but is not associated with a team the bot can resolve.

Source

Thrown at platform/tuitui/tuitui.go:222

	}
	if strings.TrimSpace(markdown) == "" {
		return fmt.Errorf("tuitui: markdown is required")
	}

	chatID := channelID
	if guessChatType(channelID) == chatTypeChannel {
		if parentID = strings.TrimSpace(parentID); parentID != "" {
			target := teamsParseChatID(channelID)
			chatID = teamsBuildChatID(target["team_id"], target["channel_id"], parentID)
		}
	} else {
		info, err := p.getChannelInfo(ctx, channelID)
		if err != nil {
			return err
		}
		teamID := stringFromAny(info["team_id"])
		if teamID == "" {
			return fmt.Errorf("tuitui: team_id not found for channel %q", channelID)
		}
		chatID = teamsBuildChatID(teamID, channelID, strings.TrimSpace(parentID))
	}
	return p.sendText(ctx, replyContext{chatID: chatID, chatType: chatTypeChannel}, markdown)
}

func (p *Platform) SendImage(ctx context.Context, replyCtx any, img core.ImageAttachment) error {
	name := img.FileName
	if name == "" {
		name = "image"
	}
	mediaID, _, err := p.uploadMedia(ctx, img.Data, img.MimeType, name, "image")
	if err != nil {
		return fmt.Errorf("tuitui: upload image: %w", err)
	}
	rctx, err := requireReplyContext(replyCtx)
	if err != nil {
		return err

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Add the bot to the team that owns the channel and grant scopes so channel info includes team_id.
  2. Verify the channel ID is correct and still active by calling getChannelInfo directly and inspecting the response.
  3. Configure an explicit team-to-channel mapping (or pass a fully-qualified teams chat ID) instead of relying on lookup.

Example fix

// before
info, _ := p.GetChannelInfo(ctx, chID) // returns no team_id
p.SendChannelPost(ctx, chID, md, "")
// after
info, err := p.GetChannelInfo(ctx, chID)
if err != nil || stringFromAny(info["team_id"]) == "" {
    return fmt.Errorf("cannot resolve team for channel %s; check bot membership", chID)
}
p.SendChannelPost(ctx, chID, md, "")
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := p.GetChannelInfo(ctx, chID)
if err == nil && stringFromAny(info["team_id"]) == "" {
    return fmt.Errorf("team not resolvable for %s", chID)
}

Type guard

func teamResolvable(info map[string]any) bool {
    tid, _ := info["team_id"].(string)
    return tid != ""
}

Try / catch

if err := p.SendChannelPost(ctx, chID, md, parent); err != nil && strings.Contains(err.Error(), "team_id not found") {
    log.Error("bot may not be a member of the owning team", "channel", chID, "cause", err)
}

Prevention

When it happens

Trigger: Calling SendChannelPost with a channel-type ID whose getChannelInfo response lacks "team_id" or returns an empty string for it.

Common situations: Posting to a channel whose team membership is not visible to the bot (missing scope or bot not added to the team); stale channel metadata after the channel was moved/archived; API returned partial channel info.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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