sipeed/picoclaw · error

seahorse assemble: %w

Error message

seahorse assemble: %w

What it means

seahorseContextManager.Assemble builds the prompt context within a token budget by calling engine.Assemble on the session DB; any engine failure is wrapped as 'seahorse assemble: %w'. Unlike engine creation (which falls back to legacy), an Assemble error propagates into the turn pipeline (pipeline_setup.go / turn_coord.go), failing that agent turn. The code just above also halves the budget when MaxTokens >= budget, so pathological budget settings surface here indirectly.

Source

Thrown at pkg/agent/context_seahorse.go:116

	if budget <= 0 {
		budget = 100000
	}

	// Reserve space for model response (spec lines 1400-1410)
	effectiveBudget := budget - req.MaxTokens
	if effectiveBudget <= 0 {
		// MaxTokens >= budget is a configuration problem
		// Use 50% as minimum to avoid guaranteed overflow
		logger.WarnCF("agent", "MaxTokens >= budget, using 50% fallback",
			map[string]any{"budget": budget, "max_tokens": req.MaxTokens})
		effectiveBudget = budget / 2
	}

	result, err := m.engine.Assemble(ctx, req.SessionKey, seahorse.AssembleInput{
		Budget: effectiveBudget,
	})
	if err != nil {
		return nil, fmt.Errorf("seahorse assemble: %w", err)
	}

	history := seahorseToProviderMessages(result)

	// Summary is already formatted as XML with system prompt addition by assembler
	return &AssembleResponse{
		History: history,
		Summary: result.Summary,
	}, nil
}

// Compact compresses conversation history via seahorse summarization.
func (m *seahorseContextManager) Compact(ctx context.Context, req *CompactRequest) error {
	if req == nil {
		return nil
	}

	// For retry (LLM overflow), use aggressive CompactUntilUnder to guarantee

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Read the wrapped engine error to distinguish I/O corruption from locking
  2. Restart picoclaw — transient SQLite locks from a crashed process clear on restart
  3. For corruption, rename/remove seahorse.db (or the affected session's data) so seahorse rebuilds it
  4. Configure the context budget above the model's max_tokens to avoid the 50%-budget fallback path
  5. Enforce one process per workspace to stop concurrent DB writers

Example fix

// config — before: budget smaller than max_tokens forces the 50% fallback
"agents": { "defaults": { "max_tokens": 8192, "context_window": 4096 } }

// after: budget exceeds max_tokens
"agents": { "defaults": { "max_tokens": 8192, "context_window": 131072 } }
Defensive patterns

Strategy: try-catch

Validate before calling

if cfg.Agents.Defaults.MaxTokens > 0 && cfg.Agents.Defaults.ContextWindow > 0 &&
    cfg.Agents.Defaults.MaxTokens >= cfg.Agents.Defaults.ContextWindow {
    return fmt.Errorf("max_tokens (%d) must be smaller than context budget (%d)",
        cfg.Agents.Defaults.MaxTokens, cfg.Agents.Defaults.ContextWindow)
}

Try / catch

resp, err := ctxMgr.Assemble(ctx, req)
if err != nil {
    if strings.Contains(err.Error(), "seahorse assemble") {
        // degrade for this turn: fall back to trimmed legacy history instead of failing the turn
        log.Printf("seahorse assemble failed (%v); using legacy assembly", err)
        resp, err = legacyMgr.Assemble(ctx, req)
    }
    if err != nil {
        return nil, err
    }
}

Prevention

When it happens

Trigger: engine.Assemble fails mid-turn: seahorse.db becomes unreadable or corrupt, rows for the session are malformed, or the engine was closed (e.g. agent Close racing an in-flight turn).

Common situations: Disk filling up or being yanked mid-run; a second picoclaw instance writing the same DB; deleting workspace files while the bot runs; schema drift after upgrading picoclaw with an old seahorse.db.

Related errors


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