vxcontrol/pentagi · error

failed to update tool call log result: %w

Error message

failed to update tool call log result: %w

What it means

UpdateLogSuccess marks a tool call as finished by calling db.UpdateToolcallFinishedResult. This error wraps the underlying SQL failure — most commonly sql.ErrNoRows because the toolcall ID does not exist (or belongs to another flow / was already finished, depending on the query), or a transient DB error.

Source

Thrown at backend/pkg/controller/tclog.go:88

	w.pub.ToolCallLogAdded(ctx, tc)

	return tc.ID, nil
}

func (w *flowToolCallLogWorker) UpdateLogSuccess(
	ctx context.Context,
	id int64,
	result string,
	durationSeconds float64,
) error {
	tc, err := w.db.UpdateToolcallFinishedResult(ctx, database.UpdateToolcallFinishedResultParams{
		Result:          result,
		DurationSeconds: durationSeconds,
		ID:              id,
	})
	if err != nil {
		return fmt.Errorf("failed to update tool call log result: %w", err)
	}

	w.pub.ToolCallLogUpdated(ctx, tc)

	return nil
}

func (w *flowToolCallLogWorker) UpdateLogFailed(
	ctx context.Context,
	id int64,
	result string,
	durationSeconds float64,
) error {
	tc, err := w.db.UpdateToolcallFailedResult(ctx, database.UpdateToolcallFailedResultParams{
		Result:          result,
		DurationSeconds: durationSeconds,
		ID:              id,
	})

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check errors.Is(err, sql.ErrNoRows) — if so the ID is wrong or the row was deleted
  2. Log and return the ID from PutLog rather than reconstructing or caching it
  3. Guard against double-update with a status check / idempotency in the calling agent code
  4. Inspect the wrapped error for constraint or connection details in the DB logs
  5. Retry only transient driver errors; missing rows will never succeed on retry

Example fix

// before
if err := worker.UpdateLogSuccess(ctx, id, result, dur); err != nil {
    return fmt.Errorf("mark success: %w", err)
}
// after
if err := worker.UpdateLogSuccess(ctx, id, result, dur); err != nil {
    if errors.Is(err, sql.ErrNoRows) {
        log.Warn().Int64("toolcall_id", id).Msg("toolcall already gone, skipping success update")
        return nil
    }
    return fmt.Errorf("mark success: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

func toolcallExists(ctx context.Context, q database.Querier, id int64) bool {
    _, err := q.GetFlowToolcall(ctx, database.GetFlowToolcallParams{ID: id, FlowID: currentFlowID})
    return err == nil
}

Type guard

func isNoRows(err error) bool { return errors.Is(err, sql.ErrNoRows) }

Try / catch

err := worker.UpdateLogSuccess(ctx, id, result, dur)
switch {
case err == nil:
    // ok
case isNoRows(err):
    log.Warn().Int64("id", id).Msg("toolcall missing; skipping success update")
case errors.Is(err, context.Canceled):
    // treat as non-fatal, result already delivered to agent
    log.Warn().Err(err).Msg("success update canceled")
default:
    return fmt.Errorf("update success: %w", err)
}

Prevention

When it happens

Trigger: Calling UpdateLogSuccess with an id that was never inserted, an id from a different flow worker, a duplicate completion after the row was already transitioned out of running status, or DB connection loss/canceled context during the UPDATE.

Common situations: Double completion when both success and failure paths fire for one call; using an ID cached from a previous flow run; DB failover mid-execution of long-running tools.

Related errors


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