vxcontrol/pentagi · error

failed to store answer for question: %w

Error message

failed to store answer for question: %w

What it means

On the 'fast path' of store-answer (where the answer fits within the embedding byte limit), the vector store rejected the add/update of the Q&A document. The error wraps the underlying store/embedding failure and is also reported to the Langfuse observation with error level.

Source

Thrown at backend/pkg/tools/search.go:295

				logger.WithError(err).Error("failed to load document")
				return "", fmt.Errorf("failed to load document: %w", err)
			}
			for i := range docs {
				if docs[i].Metadata == nil {
					docs[i].Metadata = map[string]any{}
				}
				maps.Copy(docs[i].Metadata, metadata)
				docs[i].Metadata["part_size"] = len(docs[i].PageContent)
			}
			ids, err = s.store.AddDocuments(ctx, docs)
			eventMetadata["ids"] = ids
			if err != nil {
				observation.Event(append(opts,
					langfuse.WithEventStatus(err.Error()),
					langfuse.WithEventLevel(langfuse.ObservationLevelError),
				)...)
				logger.WithError(err).Error("failed to store answer for question")
				return "", fmt.Errorf("failed to store answer for question: %w", err)
			}
		} else {
			// Slow path: Answer field exceeds embedding limit.
			// PageContent is just the answer text, so overhead = 0.
			embeddingText := truncateForEmbedding(anonymizedAnswer, s.maxEmbeddingBytes)

			id, err := storeDocumentWithEmbeddingLimit(ctx, s.db, s.embedder,
				embeddingText, anonymizedAnswer, metadata)
			if err != nil {
				observation.Event(append(opts,
					langfuse.WithEventStatus(err.Error()),
					langfuse.WithEventLevel(langfuse.ObservationLevelError),
				)...)
				logger.WithError(err).Error("failed to store answer with embedding limit")
				return "", fmt.Errorf("failed to store answer for question: %w", err)
			}
			ids = []string{id}
			docs = []schema.Document{

View on GitHub (pinned to ea665308ba)

Solutions

  1. Read the wrapped root error to distinguish embedding-provider vs database failure.
  2. Validate embedding provider credentials/quota in env config.
  3. Keep metadata values scalar (string/number/bool) to avoid store-side rejection.
  4. Retry transient DB failures with backoff; the operation is otherwise idempotent per question.

Example fix

// before
err := store.AddDocument(ctx, doc) // no retry on transient failure
// after
err := retry.Do(ctx, 3, time.Second, func() error { return store.AddDocument(ctx, doc) })
Defensive patterns

Strategy: retry

Validate before calling

if !embedProviderHealthy(ctx) {
    return errors.New("embedding provider unavailable, skipping store answer")
}
for k, v := range metadata {
    switch v.(type) {
    case string, int, int64, float64, bool, nil:
    default:
        return fmt.Errorf("metadata %q has non-scalar type %T", k, v)
    }
}

Try / catch

err := storeAnswerFastPath(ctx, action)
if err != nil && strings.Contains(err.Error(), "failed to store answer for question") {
    if isRetryable(errors.Unwrap(err)) {
        return backoffRetry(ctx, 3, time.Second, storeAnswerFastPath, action)
    }
    observation.Event(langfuse.WithEventStatus(err.Error()))
    return err
}

Prevention

When it happens

Trigger: `store answer` invoked with an anonymized answer at or under `maxEmbeddingBytes`; the subsequent `store.AddDocument`-style call fails due to DB errors, embedding API errors, or invalid metadata types.

Common situations: Embedding provider quota exhausted or key invalid; transient Postgres disconnects; metadata containing non-scalar values the store rejects; concurrent migrations locking the documents table.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/0996f71fd9a6c82b. Report an issue: GitHub.