chenhg5/cc-connect · error

relay: project %q is not bound in this chat. Available targe

Error message

relay: project %q is not bound in this chat. Available targets: %s (use the exact name)

What it means

Thrown by RelayManager.Send when a binding exists for the chat but binding.Bots has no entry for req.To. The error lists the other bound project names so the caller can correct the target name. Bot map keys are exact project names, so near-matches fail.

Source

Thrown at core/relay.go:228

	rm.mu.RLock()
	binding := rm.bindings[chatID]
	targetEngine := rm.engines[req.To]
	sourceEngine := rm.engines[req.From]
	visibility := rm.visibility
	rm.mu.RUnlock()

	if binding == nil {
		return nil, fmt.Errorf("relay: no binding for this chat. Use /bind <project> first")
	}
	if _, ok := binding.Bots[req.To]; !ok {
		var bound []string
		for proj := range binding.Bots {
			if proj != req.From {
				bound = append(bound, proj)
			}
		}
		return nil, fmt.Errorf("relay: project %q is not bound in this chat. Available targets: %s (use the exact name)", req.To, strings.Join(bound, ", "))
	}
	if targetEngine == nil {
		return nil, fmt.Errorf("relay: target engine %q not found (is the project running?)", req.To)
	}

	fromName := req.From
	if binding.Bots[req.From] != "" {
		fromName = binding.Bots[req.From]
	}
	toName := req.To
	if binding.Bots[req.To] != "" {
		toName = binding.Bots[req.To]
	}

	// Post the forwarded message to the group chat for visibility.  The
	// default target is "<platform>:<chatID>:relay"; platforms that
	// understand thread / topic semantics can override this by
	// implementing core.RelayGroupVisibilityTarget on their Platform impl.

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use one of the project names listed in 'Available targets' exactly (case-sensitive)
  2. Re-run /bind in this chat to add the target project to the binding
  3. Check config.toml for the project's registered name (the engine name), not the bot display name
  4. If the target was recently unbound, rebind it and retry

Example fix

// before: display name instead of project name
rm.Send(ctx, core.RelayRequest{From: "frontend", To: "Backend Bot", SessionKey: key, Message: "hi"})
// after: exact bound project name
rm.Send(ctx, core.RelayRequest{From: "frontend", To: "backend", SessionKey: key, Message: "hi"})
Defensive patterns

Strategy: validation

Validate before calling

func canRelayTo(binding *core.RelayBinding, to string) bool {
	_, ok := binding.Bots[to]
	return ok
}
// surface binding.Bots keys (minus the source) to the user before sending

Type guard

targets := make([]string, 0, len(binding.Bots))
for proj := range binding.Bots { if proj != req.From { targets = append(targets, proj) } }
if !slices.Contains(targets, req.To) { return fmt.Errorf("%q not bound; valid: %v", req.To, targets) }

Try / catch

resp, err := rm.Send(ctx, req)
if err != nil {
	var avail string
	if _, gerr := fmt.Sscanf(err.Error(), "relay: project %q is not bound", new(string)); gerr == nil || strings.Contains(err.Error(), "Available targets") {
		reply("Unknown target. " + err.Error())
		return
	}
	return err
}

Prevention

When it happens

Trigger: RelayRequest.To is misspelled, uses a display/bot name instead of the project name, refers to a project bound in a different chat, or the target project was unbound after /bind. Any To not present in binding.Bots triggers it.

Common situations: Typing /to ProjB when the bound name is projb; using the bot's display name rather than the registered project name; referencing a project that is bound in another chat but not this one; stale autocomplete after rebinding.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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