sipeed/picoclaw · error

seahorse: create engine: %w

Error message

seahorse: create engine: %w

What it means

When agents.defaults.context_manager is "seahorse", picoclaw builds a seahorse engine backed by a SQLite database at <default-agent-workspace>/sessions/seahorse.db (seahorse.NewEngine). Any failure creating/opening that DB is wrapped here. In the standard resolveContextManager path this failure is logged and picoclaw falls back to the legacy context manager, so the impact is losing seahorse retrieval, not a crash.

Source

Thrown at pkg/agent/context_seahorse.go:45

func newSeahorseContextManager(_ json.RawMessage, al *AgentLoop) (ContextManager, error) {
	if al == nil {
		return nil, fmt.Errorf("seahorse: AgentLoop is required")
	}

	// Resolve workspace for DB path
	// DB stores session data, so it goes in sessions/ directory
	agent := al.registry.GetDefaultAgent()
	dbPath := agent.Workspace + "/sessions/seahorse.db"

	// Create CompleteFn from provider
	completeFn := providerToCompleteFn(agent.Provider, agent.Model)

	// Create engine
	engine, err := seahorse.NewEngine(seahorse.Config{
		DBPath: dbPath,
	}, completeFn)
	if err != nil {
		return nil, fmt.Errorf("seahorse: create engine: %w", err)
	}

	mgr := &seahorseContextManager{
		engine:   engine,
		sessions: agent.Sessions,
		al:       al,
	}

	// Register seahorse tools with the agent's tool registry
	retrieval := mgr.engine.GetRetrieval()
	al.RegisterTool(seahorse.NewGrepTool(retrieval))
	al.RegisterTool(seahorse.NewExpandTool(retrieval))

	// Bootstrap all existing sessions at startup
	if agent.Sessions != nil {
		ctx := context.Background()
		for _, sessionKey := range agent.Sessions.ListSessions() {
			mgr.bootstrapSession(ctx, sessionKey)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Read the wrapped error to classify: permission vs corrupt vs database-is-locked
  2. mkdir -p <workspace>/sessions and fix ownership/permissions (writable by the picoclaw user)
  3. Give each picoclaw process its own workspace, or stop the second instance holding the SQLite lock
  4. Rename or delete a corrupt seahorse.db so it is rebuilt (context cache lost; JSONL session history is separate)
  5. Confirm the platform is supported — seahorse is compiled out on mipsle/netbsd/freebsd-arm

Example fix

# before — workspace/sessions missing or read-only
ls -ld workspace/sessions  # missing or 0555

# after
mkdir -p workspace/sessions && chmod 755 workspace/sessions && chown picoclaw: workspace/sessions
Defensive patterns

Strategy: try-catch

Validate before calling

agent := al.GetRegistry().GetDefaultAgent()
dir := filepath.Join(agent.Workspace, "sessions")
if err := os.MkdirAll(dir, 0o755); err != nil {
    return fmt.Errorf("prepare seahorse dir: %w", err)
}
probe, err := os.OpenFile(filepath.Join(dir, "seahorse.db"), os.O_RDWR|os.O_CREATE, 0o644)
if err != nil {
    return fmt.Errorf("seahorse.db not writable: %w", err)
}
probe.Close()

Try / catch

cm, err := seahorse.NewEngine(seahorse.Config{DBPath: dbPath}, completeFn)
if err != nil {
    // log and degrade: keep running with the legacy context manager
    log.Printf("seahorse unavailable (%v); falling back to legacy context", err)
    return &legacyContextManager{al: al}, nil
}

Prevention

When it happens

Trigger: context_manager=seahorse selected and the SQLite DB cannot be created or opened: sessions/ directory missing or not writable under the workspace, disk full, seahorse.db corrupt, or the file locked by another picoclaw process sharing the workspace.

Common situations: Workspace on a read-only volume (containers); two instances pointed at one workspace; partially copied workspace with a corrupt seahorse.db; restrictive fs permissions or SELinux denials under ~/.picoclaw/workspace.

Related errors


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