sipeed/picoclaw · error

no agent available for route (agent_id=%s)

Error message

no agent available for route (agent_id=%s)

What it means

Every inbound message is routed: AgentRegistry.ResolveRoute maps the inbound context (channel/chat) to a ResolvedRoute with an AgentID; the loop then tries GetAgent(route.AgentID) and falls back to GetDefaultAgent(). This error fires when the routed agent_id does not exist in the registry AND no fallback agent exists — there is literally no agent to hand the message to.

Source

Thrown at pkg/agent/agent_message.go:230

				"session_key": opts.Dispatch.SessionKey,
				"skills":      strings.Join(pending, ","),
			})
	}

	return al.runAgentLoop(ctx, agent, opts)
}

func (al *AgentLoop) resolveMessageRoute(msg bus.InboundMessage) (routing.ResolvedRoute, *AgentInstance, error) {
	registry := al.GetRegistry()
	inboundCtx := normalizedInboundContext(msg)
	route := registry.ResolveRoute(inboundCtx)

	agent, ok := registry.GetAgent(route.AgentID)
	if !ok {
		agent = registry.GetDefaultAgent()
	}
	if agent == nil {
		return routing.ResolvedRoute{}, nil, fmt.Errorf("no agent available for route (agent_id=%s)", route.AgentID)
	}

	return route, agent, nil
}

func (al *AgentLoop) allocateRouteSession(route routing.ResolvedRoute, msg bus.InboundMessage) session.Allocation {
	return session.AllocateRouteSession(session.AllocationInput{
		AgentID:       route.AgentID,
		Context:       normalizedInboundContext(msg),
		SessionPolicy: route.SessionPolicy,
	})
}

func (al *AgentLoop) processSystemMessage(
	ctx context.Context,
	msg bus.InboundMessage,
) (string, error) {
	if msg.Channel != "system" {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Look at the agent_id in the message and either add that agent to config or fix the routing entry that references it
  2. Keep at least one healthy agent registered so the GetDefaultAgent fallback can absorb unknown routes
  3. Validate routing rules against agents.list ids at startup (CI config lint)
  4. Restart picoclaw after fixing config; routes are resolved per message so no cached state is involved

Example fix

// config — before: route points at nonexistent agent id
"routing": { "rules": [ { "channel": "telegram", "agent_id": "asistant" } ] }
"agents": { "list": [ { "id": "assistant", "model_name": "gpt-4o-mini" } ] }

// after: ids match
"routing": { "rules": [ { "channel": "telegram", "agent_id": "assistant" } ] }
"agents": { "list": [ { "id": "assistant", "model_name": "gpt-4o-mini" } ] }
Defensive patterns

Strategy: validation

Validate before calling

ids := al.GetRegistry().ListAgentIDs()
known := make(map[string]bool, len(ids))
for _, id := range ids {
    known[id] = true
}
for _, rule := range cfg.Routing.Rules { // adapt to your routing config shape
    if rule.AgentID != "" && !known[rule.AgentID] {
        return fmt.Errorf("routing rule references unknown agent %q", rule.AgentID)
    }
}

Prevention

When it happens

Trigger: A routing rule or channel-to-agent mapping resolves to an agent id that is not in agents.list, while the registry is empty or contains no other usable agent for the fallback. The %s in the message shows the unresolved agent_id.

Common situations: Typo in a routing rule's agent id; an agent removed or renamed in config while routing rules still point at the old id; agents.list failed to load (provider errors) so even the fallback is missing.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/57b3bc8ce9bb12fe. Report an issue: GitHub.