libnyanpasu/clash-nyanpasu · error

effect function failed: {e:#?}

Error message

effect function failed: {e:#?}

What it means

In with_pending_state_inner, the effect function of a pending state transaction failed, so the coordinator rolls back the transaction (RollbackReason::CoordinatorError) and returns the original error. The message wraps the effect error for the rollback record.

Source

Thrown at backend/nyanpasu-core/src/state/coordinator.rs:380

            },
            None => effect_fn(new_state).await.map_err(WithEffectError::Effect),
        };
        match effect_result {
            Ok(result) => {
                if tx.commit().await.is_err() {
                    let actual = self.sync_change_id_after_cas_mismatch();
                    return Err(WithEffectError::State(
                        StateChangedError::StateCasMismatch {
                            expected: current_state.version,
                            actual,
                        },
                    ));
                }
                self.mark_change_id_committed(next_changed_id);
                Ok((result, report))
            }
            Err(e) => {
                tx.rollback(RollbackReason::CoordinatorError(Arc::new(anyhow!(
                    "effect function failed: {e:#?}"
                ))))
                .await;
                Err(e)
            }
        }
    }
}

// -- Builder --

pub struct StateCoordinatorBuilder<T: Clone + Send + Sync + 'static> {
    notify_strategy: NotifyStrategy,
    subscribers: IndexMap<SubscriberName<'static>, ArcStateSubscriber<T>>,
}

impl<T: Clone + Send + Sync + 'static> Default for StateCoordinatorBuilder<T> {
    fn default() -> Self {

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Read the wrapped inner error to find the actual failing effect step.
  2. Retry the operation once the underlying cause (core down, disk error) is resolved; state was rolled back so it is safe to redo.
  3. Move fragile side effects out of the effect closure or make them idempotent.
  4. Use with_pending_state_timeout with a generous timeout if the effect is slow rather than failing.

Example fix

// before
coordinator.with_pending_state(|tx| async {
    restart_core().await?; // failure rolls everything back
    Ok(())
})
// after
coordinator.with_pending_state(|tx| async {
    if let Err(e) = restart_core().await {
        log::error!("core restart failed, state kept: {e}"); // report degraded, don't fail effect
    }
    Ok(())
})
Defensive patterns

Strategy: retry

Validate before calling

// pre-check downstream dependencies before running the effect
async fn can_run_effect() -> bool {
    core_client.ping().await.is_ok() && config_dir_writable()
}

Try / catch

match coordinator.with_pending_state(effect_fn).await {
    Err(e) => {
        log::error!("effect failed, transaction rolled back: {e:#}");
        // state is consistent; fix cause and retry
        retry_with_backoff(|| coordinator.with_pending_state(effect_fn)).await
    }
    Ok(v) => v,
}

Prevention

When it happens

Trigger: Calling with_pending_state / with_pending_state_timeout where the post-prepare effect_fn fails — e.g. runtime config generation, core restart, or IPC side effect returns Err.

Common situations: Core process unavailable during a config patch, filesystem errors writing generated config, or timeouts in downstream actor calls triggered from the effect.

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 libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/38cfb346839640e6. Report an issue: GitHub.