sipeed/picoclaw · error

conversation not found for %s after ingest

Error message

conversation not found for %s after ingest

What it means

Invariant violation: engine.Ingest returned success, but the follow-up GetConversationBySessionKey returned (nil, nil) — no error, yet no conversation row for sessionKey 'locomo-<sampleID>'. The harness treats that as unrecoverable because scoped retrieval needs the conversation ID. In practice this points to an engine-side consistency gap (async commit, transaction not yet visible) rather than a caller mistake.

Source

Thrown at cmd/membench/ingest.go:74

				Role:       "user",
				Content:    content,
				TokenCount: len(turn.Text) / 4,
			})
		}

		// Ingest all turns for this sample
		_, err := engine.Ingest(ctx, sessionKey, msgs)
		if err != nil {
			return nil, fmt.Errorf("ingest sample %s: %w", sample.SampleID, err)
		}

		// Get the conversation ID for scoped retrieval
		conv, err := store.GetConversationBySessionKey(ctx, sessionKey)
		if err != nil {
			return nil, fmt.Errorf("get conversation for %s: %w", sample.SampleID, err)
		}
		if conv == nil {
			return nil, fmt.Errorf("conversation not found for %s after ingest", sample.SampleID)
		}
		convMap[sample.SampleID] = conv.ConversationID
		log.Printf("Ingested sample %s: %d turns, convID=%d", sample.SampleID, len(turns), conv.ConversationID)
	}

	log.Printf("Seahorse ingestion complete: %d samples, %d conversations", len(samples), len(convMap))
	return &SeahorseIngestResult{
		Engine:  engine,
		ConvMap: convMap,
	}, nil
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Pin/upgrade to an engine version where Ingest is synchronous with respect to GetConversationBySessionKey
  2. Log len(msgs) before Ingest — an empty turn list is the classic silent no-row case
  3. Retry the lookup briefly (eventual visibility) before declaring failure
  4. Report as an engine bug with the sample ID if it reproduces with non-empty input

Example fix

// before
if conv == nil {
    return nil, fmt.Errorf("conversation not found for %s after ingest", sample.SampleID)
}

// after
if conv == nil {
    var err error
    for i := 0; i < 3 && conv == nil; i++ {
        time.Sleep(100 * time.Millisecond)
        conv, err = store.GetConversationBySessionKey(ctx, sessionKey)
        if err != nil { break }
    }
    if conv == nil {
        return nil, fmt.Errorf("conversation not found for %s after ingest (turns=%d)", sample.SampleID, len(msgs))
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if len(msgs) == 0 {
    log.Printf("sample %s has no turns after filtering; skipping", sample.SampleID)
    continue
}

Try / catch

if conv == nil {
    for i := 0; i < 3; i++ {
        time.Sleep(100 * time.Millisecond)
        conv, err = store.GetConversationBySessionKey(ctx, sessionKey)
        if err != nil || conv != nil { break }
    }
    if conv == nil {
        return nil, fmt.Errorf("engine bug: no conversation for %s after ingest (turns=%d)", sample.SampleID, len(msgs))
    }
}

Prevention

When it happens

Trigger: Engine writes the conversation asynchronously/deferred so the row is not yet visible at read time; sessionKey normalization mismatch between Ingest and lookup; engine bug dropping empty-conversation creation when all messages filter out.

Common situations: Upgrading the seahorse engine to a version that changed visibility semantics; ingesting a sample whose turns list is empty after filtering; DB read happening on a different connection/snapshot than the write.

Related errors


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