sipeed/picoclaw · error
error starting heartbeat service: %w
Error message
error starting heartbeat service: %w
What it means
The gateway failed while bringing up the heartbeat service during initial startup. HeartbeatService.Start() is wrapped with fmt.Errorf so any failure aborts gateway boot; in the current implementation Start() only returns non-nil on future internal changes (it returns nil when already running or disabled), so this is a defensive guard.
Source
Thrown at pkg/gateway/gateway.go:441
cfg,
)
if err != nil {
return nil, fmt.Errorf("error setting up cron service: %w", err)
}
if err = runningServices.CronService.Start(); err != nil {
return nil, fmt.Errorf("error starting cron service: %w", err)
}
fmt.Println("✓ Cron service started")
runningServices.HeartbeatService = heartbeat.NewHeartbeatService(
cfg.WorkspacePath(),
cfg.Heartbeat.Interval,
cfg.Heartbeat.Enabled,
)
runningServices.HeartbeatService.SetBus(msgBus)
runningServices.HeartbeatService.SetHandler(createHeartbeatHandler(agentLoop))
if err = runningServices.HeartbeatService.Start(); err != nil {
return nil, fmt.Errorf("error starting heartbeat service: %w", err)
}
fmt.Println("✓ Heartbeat service started")
runningServices.MediaStore = media.NewFileMediaStoreWithCleanup(media.MediaCleanerConfig{
Enabled: cfg.Tools.MediaCleanup.Enabled,
MaxAge: time.Duration(cfg.Tools.MediaCleanup.MaxAge) * time.Minute,
Interval: time.Duration(cfg.Tools.MediaCleanup.Interval) * time.Minute,
})
if fms, ok := runningServices.MediaStore.(*media.FileMediaStore); ok {
fms.Start()
}
runningServices.ChannelManager, err = channels.NewManager(
cfg,
msgBus,
runningServices.MediaStore,
channels.WithRuntimeEvents(agentLoop.RuntimeEventBus()),
)View on GitHub (pinned to 49183d7e8d)
Solutions
- Verify workspace path exists and is writable (heartbeat builds state.NewManager(workspace))
- Check config heartbeat.enabled and heartbeat.interval values in config.json
- Inspect the wrapped error chain with errors.Unwrap to find the real cause
- If running a fork, confirm HeartbeatService.Start() in pkg/heartbeat/service.go matches upstream
Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := os.Stat(cfg.WorkspacePath()); err != nil {
log.Fatalf("workspace not ready before gateway start: %v", err)
} Try / catch
if err = runningServices.HeartbeatService.Start(); err != nil {
return fmt.Errorf("error starting heartbeat service: %w", err)
} Prevention
- Pre-validate workspace existence and writability before boot
- Keep heartbeat config keys (enabled, interval) within documented ranges
When it happens
Trigger: Calling startServices when heartbeat.NewHeartbeatService(...).Start() returns an error after SetBus/SetHandler wiring; today Start()'s code paths (already running, disabled, spawn goroutine) all return nil, making the wrap nearly unreachable without a code change.
Common situations: A modified/older fork where Start validates the workspace or state manager; corrupted workspace/state directory used by state.NewManager(cfg.WorkspacePath()); heartbeat.enabled misconfigured alongside a read-only workspace.
Related errors
- error creating channel manager: %w
- error starting channels: %w
- must be in range 1-65535
- failed to load MCP servers: %w
- no default agent for heartbeat
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/25b0a46010781bde.
Report an issue: GitHub.