{"record":{"id":"90d1f247a8394f6d","repo":"sipeed/picoclaw","slug":"no-default-agent-for-heartbeat","errorCode":null,"errorMessage":"no default agent for heartbeat","messagePattern":"no default agent for heartbeat","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/agent_message.go","lineNumber":81,"sourceCode":"\t}\n\n\treturn al.processMessage(ctx, msg)\n}\n\nfunc (al *AgentLoop) ProcessHeartbeat(\n\tctx context.Context,\n\tcontent, channel, chatID string,\n) (string, error) {\n\tif err := al.ensureHooksInitialized(ctx); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := al.ensureMCPInitialized(ctx); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tagent := al.GetRegistry().GetDefaultAgent()\n\tif agent == nil {\n\t\treturn \"\", fmt.Errorf(\"no default agent for heartbeat\")\n\t}\n\tdispatch := DispatchRequest{\n\t\tSessionKey:  \"heartbeat\",\n\t\tUserMessage: content,\n\t}\n\tif channel != \"\" || chatID != \"\" {\n\t\tdispatch.InboundContext = &bus.InboundContext{\n\t\t\tChannel:  channel,\n\t\t\tChatID:   chatID,\n\t\t\tChatType: \"direct\",\n\t\t\tSenderID: \"heartbeat\",\n\t\t}\n\t}\n\treturn al.runAgentLoop(ctx, agent, processOptions{\n\t\tDispatch:             dispatch,\n\t\tDefaultResponse:      defaultResponse,\n\t\tEnableSummary:        false,\n\t\tSendResponse:         false,","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/agent_message.go#L63-L99","documentation":"HandleHeartbeat dispatches periodic heartbeat content through the default agent. AgentRegistry.GetDefaultAgent (pkg/agent/registry.go:174) returns the configured default, else any registered agent, else nil. This error means the registry is completely empty — no agents were constructed from config, so there is nothing to dispatch the heartbeat to.","triggerScenarios":"The heartbeat timer fires (or HandleHeartbeat is called directly) while the AgentRegistry has zero agents: agents.list empty/absent in config, or agent construction failed earlier so nothing was registered.","commonSituations":"Minimal first-run config with providers but no agents section; heartbeat feature enabled while agents are disabled; agent construction silently skipped after a provider/model config error; unit tests building an AgentLoop without registry entries.","solutions":["Add at least one agent to config (agents.list entry with a model/provider) so the registry is non-empty","Check startup logs for earlier agent-construction errors (bad provider, missing API key) that left the registry empty","If you do not want periodic agent self-checks, disable the heartbeat feature instead of leaving it on","In custom embeddings, register an agent with the registry before enabling heartbeats"],"exampleFix":"// config — before\n\"agents\": { \"defaults\": {}, \"list\": [] }\n\n// after\n\"agents\": {\n  \"defaults\": { \"model_name\": \"gpt-4o-mini\", \"provider\": \"openai\" },\n  \"list\": [ { \"id\": \"main\", \"model_name\": \"gpt-4o-mini\", \"provider\": \"openai\" } ]\n}","handlingStrategy":"validation","validationCode":"if al.GetRegistry().GetDefaultAgent() == nil {\n    return fmt.Errorf(\"no agents registered: add an agents.list entry before enabling heartbeat\")\n}\n// only now start the heartbeat scheduler","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Gate heartbeat scheduling on a non-empty agent registry at startup","Fail config validation when heartbeat is enabled but agents.list is empty","Check agent construction logs after config changes — an empty registry is usually downstream of a provider error"],"tags":["go","picoclaw","agents","registry","heartbeat","config"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}