itsfatduck/optimizerDuck · error · StepExecutionException

Service verify failed for

Error message

Service verify failed for {ServiceName}: expected {OriginalStartupType}, actual={actual.Value}

What it means

ServiceRevertStep.ExecuteAsync throws this when verification succeeds in querying but the actual startup type differs from OriginalStartupType recorded at apply time. The sc.exe config command did not produce the expected state even though it did not error.

Solutions

  1. Re-run the revert; if it fails again, something is actively resetting the value — find and disable that agent.
  2. Check Group Policy results (gpresult) for service policies and remove conflicting ones.
  3. Set the type manually: sc config <ServiceName> start= <auto|delayed-auto|manual|disabled> to the desired original, then discard the revert file.
  4. Re-apply then revert the optimization to refresh the recorded OriginalStartupType if the real original changed.

Example fix

// before
sc config WSearch start= disabled
// after (match the recorded original)
sc config WSearch start= delayed-auto
Defensive patterns

Strategy: validation

Validate before calling

var (actual, _) = await ServiceProcessService.GetStartupTypeAsync(serviceName, logger);
bool matches = actual == originalStartupType; // check before assuming revert needed

Try / catch

catch (StepExecutionException ex) when (ex.Message.Contains("expected "))
{ logger.LogError("Something is resetting {Service}; find the policy/vendor agent before retrying", serviceName); }

Prevention

When it happens

Trigger: Service startup type changed back by Group Policy, a vendor installer, or Windows itself after the revert command; the revert targeted a service whose startup type was changed again after apply (OriginalStartupType no longer the true original); delayed-auto conversions not applied as expected.

Common situations: Corporate GPO resetting service start types at refresh; Windows servicing re-enabling a service; user manually changed the service in services.msc between apply and revert so the recorded original no longer matches intent.

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 itsfatduck/optimizerDuck@36acf585ae (2026-09-13). Data as JSON: /api/errors/5946997500cfe612. Report an issue: GitHub.

Appendix: source

Thrown at optimizerDuck/Domain/Revert/Steps/ServiceRevertStep.cs:75

        }

        var (actual, notFound) = await ServiceProcessService
            .GetStartupTypeAsync(ServiceName, opCall.Logger)
            .ConfigureAwait(false);

        // a missing service has nothing to restore.
        if (notFound)
            return true;

        // null without NotFound means the query failed; never report an unverified restore.
        if (actual is null)
            throw new StepExecutionException(
                $"Service verify failed for {ServiceName}: could not query the current startup type.",
                null
            );

        if (actual.Value != OriginalStartupType)
            throw new StepExecutionException(
                $"Service verify failed for {ServiceName}: expected {OriginalStartupType}, actual={actual.Value}",
                null
            );
        return true;
    }

    /// <inheritdoc />
    public JObject ToData()
    {
        return new JObject
        {
            [nameof(ServiceName)] = ServiceName,
            [nameof(OriginalStartupType)] = OriginalStartupType.ToString(),
        };
    }

    /// <summary>
    ///     Deserializes a <see cref="ServiceRevertStep" /> from JSON data.

View on GitHub (pinned to 36acf585ae)