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

  1. Verify workspace path exists and is writable (heartbeat builds state.NewManager(workspace))
  2. Check config heartbeat.enabled and heartbeat.interval values in config.json
  3. Inspect the wrapped error chain with errors.Unwrap to find the real cause
  4. 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

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


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