micro/go-micro · error

flow %s checkpoint save: %w

Error message

flow %s checkpoint save: %w

What it means

f.save persists the run record through the configured Checkpoint. If checkpoint.Save fails, the save method logs the error and returns it wrapped as 'flow %s checkpoint save: %w' so callers of Start/Resume/ResumeWith see both the flow name and the underlying store error. Note save is a no-op returning nil when no checkpoint is configured, so this error only occurs on an actual save path with a configured store.

Source

Thrown at flow/steps.go:716

func applyVerificationRecord(record *StepRecord, verification Verification) {
	if verification.Passed {
		record.VerificationStatus = "passed"
	}
	if verification.Feedback != "" {
		record.VerificationNote = truncate(verification.Feedback, 200)
		if !verification.Passed {
			record.VerificationStatus = "failed"
		}
	}
}

func (f *Flow) save(ctx context.Context, run Run) error {
	if f.checkpoint == nil {
		return nil
	}
	if err := f.checkpoint.Save(ctx, run); err != nil {
		f.log.Logf(logger.ErrorLevel, "Flow %s checkpoint save: %v", f.name, err)
		return fmt.Errorf("flow %s checkpoint save: %w", f.name, err)
	}
	return nil
}

func validateSteps(steps []Step) error {
	seen := make(map[string]struct{}, len(steps))
	for i, step := range steps {
		if step.Name == "" {
			return fmt.Errorf("flow: step %d has an empty name", i)
		}
		if _, ok := seen[step.Name]; ok {
			return fmt.Errorf("flow: duplicate step name %q", step.Name)
		}
		seen[step.Name] = struct{}{}
	}
	return nil
}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Read the wrapped cause after the flow name in the message and fix the underlying checkpoint store error (connectivity, auth, permissions).
  2. Verify the Checkpoint store configuration: address, bucket/prefix, and credentials.
  3. Increase the context deadline for the flow operation so saves aren't cut off by caller timeouts.
  4. Add health checks/retries on the store backend, and alert on the logged 'Flow %s checkpoint save' error lines.
  5. Confirm the Run record (State.Data, StepResults) fits backend limits; truncate large payloads.

Example fix

// before
f := flow.New(flow.Checkpoint(store.New(store.Nodes("bad-host:9999"))))

// after
f := flow.New(flow.Checkpoint(store.New(
  store.Nodes("localhost:6379"),
  store.Database("flows"),
))) // reachable, writable store
Defensive patterns

Strategy: retry

Validate before calling

// verify store writability before building the flow
r := store.New(store.Nodes(addr))
if err := r.Write(ctx, "healthcheck-key", []byte("ok")); err != nil {
    return fmt.Errorf("checkpoint store not writable: %w", err)
}

Try / catch

if err := f.Start(ctx, in); err != nil {
    var csErr *checkpointSaveError
    if strings.Contains(err.Error(), "checkpoint save: ") {
        // transient store issue: backoff and retry the operation
        return retryWithBackoff(ctx, func() error { _, err := f.Start(ctx2, in); return err })
    }
    return err
}

Prevention

When it happens

Trigger: Any run state transition (started, step progress, waiting, done, failed) calling f.save when the Checkpoint backend's Save returns an error: store unreachable, serialization failure, permission denied, context canceled/deadline exceeded.

Common situations: Store service down or network partitioned during a run; store bucket/prefix misconfigured or lacking write permissions; run record too large for the backend; context deadline from the caller expiring mid-save; auth token expired for a hosted store.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/0b43e113b741f680. Report an issue: GitHub.