iflytek/astron-agent · error · SaveSnapshotDidNotStabilizeError

SaveSnapshotDidNotStabilizeError

Error message

SaveSnapshotDidNotStabilizeError

What it means

SaveSnapshotDidNotStabilizeError is thrown by the workflow save coordinator when the persisted snapshot never converges to a stable state: after each persist, the captured workflow snapshot either changed fingerprint or the revision advanced again, and the loop exhausts maxStabilizationAttempts retries. It indicates edits keep arriving faster than the save loop can persist them, or the snapshot fingerprint is non-deterministic.

Solutions

  1. Check persistSnapshot for writes back into workflow state (IDs, timestamps, normalization) that change the fingerprint after save; make it side-effect-free.
  2. Make captureSnapshot deterministic — exclude volatile fields (updatedAt, runtime node ids) from the fingerprint.
  3. Increase maxStabilizationAttempts or add debounce to auto-save so bursts of edits coalesce.
  4. Catch the error in the caller (flush/promise consumers) and surface a non-blocking 'save failed' toast with a retry action instead of breaking the editor.

Example fix

// before: fingerprint includes volatile fields
const fingerprint = hash(JSON.stringify(store.getState().workflow));
// after: exclude volatile fields from fingerprint
const { updatedAt, runtimeMeta, ...stable } = store.getState().workflow;
const fingerprint = hash(JSON.stringify(stable));
Defensive patterns

Strategy: validation

Validate before calling

const knownRoutes = ['/api/v1/agents', '/api/v1/bots']; if (!knownRoutes.some(r => url.startsWith(r))) console.warn('unregistered route', url);

Prevention

When it happens

Trigger: Rapid successive edits to the workflow while auto-save runs, so every persist completes with a newer revision than the one persisted; or captureSnapshot() returning a different fingerprint each time for identical state (non-deterministic serialization).

Common situations: Users typing/dragging in the workflow editor continuously while auto-save fires; a persistSnapshot implementation that mutates state (e.g. writing back IDs/timestamps into the store, causing a new fingerprint each cycle); debouncing misconfigured so saves are scheduled on every keystroke.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/9d25449ceefc09cd. Report an issue: GitHub.

Appendix: source

Thrown at console/frontend/src/components/workflow/store/workflow-save-coordinator.ts:112

      const currentSnapshot = options.captureSnapshot();
      const isStable =
        revision === requestedRevision &&
        currentSnapshot?.fingerprint === snapshot.fingerprint;

      // Writes are serialized, so every successful response is safe to apply
      // in order. A later edit is persisted before the flush barrier resolves.
      if (options.isSnapshotCurrent?.(snapshot.value) !== false) {
        options.onPersisted?.(result, snapshot.value);
      }
      if (isStable) {
        persistedRevision = revision;
        persistedFingerprint = snapshot.fingerprint;
        return;
      }

      attempts += 1;
      if (attempts >= maxStabilizationAttempts) {
        throw new SaveSnapshotDidNotStabilizeError();
      }

      // If an edit changed the observable snapshot without explicitly
      // scheduling auto-save, the worker still treats it as a newer revision.
      if (revision === requestedRevision) {
        requestedRevision += 1;
      }
    }
  };

  const ensureRun = (): Promise<void> => {
    const runGeneration = generation;
    if (activeRun) {
      if (activeRun.generation === runGeneration) {
        return activeRun.promise;
      }
      return activeRun.promise.catch(() => undefined).then(() => ensureRun());
    }

View on GitHub (pinned to 5e758547a8)