itsfatduck/optimizerDuck · error · InvalidOperationException

Failed to create registry key

Error message

Failed to create registry key: {root.Name}\{currentPath}

What it means

RegistryService.CreateSubKeyTrack throws InvalidOperationException when RegistryKey.CreateSubKey(part, true) returns null while walking/creating the key path segment by segment. CreateSubKey returning null means Windows refused to create the key — almost always because the parent is not writable or the path depth is invalid, despite the operation normally creating-or-opening.

Solutions

  1. Run the app as administrator (app.manifest requires it — confirm the process is actually elevated).
  2. Check the key's ACLs in regedit (Permissions) and take ownership/adjust if a policy locked it.
  3. Verify the registry path in the optimization definition for empty segments or invalid characters/length.
  4. If Windows protects the key (WRP), skip the optimization — it is not applicable on that system.

Example fix

// before
next = current.CreateSubKey(part, true) ?? throw ...;
// after (guard writability first)
if (!root.CanWrite)
    throw new InvalidOperationException($"Registry hive not writable (elevation required): {root.Name}");
next = current.CreateSubKey(part, true) ?? throw ...;
Defensive patterns

Strategy: try-catch

Validate before calling

using var probe = Registry.LocalMachine.OpenSubKey(subPath);
bool writable = probe != null &&
    (new RegistrySecurity()).Equals(null) is false; // simpler: attempt CreateSubKey in try/catch

Type guard

static bool HiveWritable(RegistryKey root) =>
    Environment.IsPrivilegedProcess;

Try / catch

try { registryService.CreateSubKey(path, opCall); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Failed to create registry key"))
{ logger.LogError("Elevation or ACLs blocked key creation: {Msg}", ex.Message); }

Prevention

When it happens

Trigger: Creating a missing subkey during apply (e.g. writing a value into a key tree that does not exist) where the intermediate key cannot be created: parent opened without write access (HKLM without elevation), key protected by ACL/WRP, or a path part empty/invalid length (exceeds 255 chars per key name).

Common situations: Running the app unelevated and optimizing an HKLM setting; Windows Resource Protection guarding system keys; corporate policy ACLs denying write; extremely long optimization registry paths hitting the key-name limit.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of itsfatduck/optimizerDuck@36acf585ae (2026-09-13). Data as JSON: /api/errors/a7301b2d738a9f37. Report an issue: GitHub.

Appendix: source

Thrown at optimizerDuck/Services/Optimization/Providers/RegistryService.cs:981

    {
        var parts = subPath.Split('\\', StringSplitOptions.RemoveEmptyEntries);

        var current = root;
        var ownsCurrent = false;
        var currentPath = string.Empty;

        try
        {
            foreach (var part in parts)
            {
                currentPath = currentPath.Length == 0 ? part : $"{currentPath}\\{part}";

                var next = current.OpenSubKey(part, true);
                if (next == null)
                {
                    next =
                        current.CreateSubKey(part, true)
                        ?? throw new InvalidOperationException(
                            $"Failed to create registry key: {root.Name}\\{currentPath}"
                        );

                    createdSubKeys.Add($"{root.Name}\\{currentPath}");
                    logger?.LogDebug(
                        "Created registry subkey: {Path}",
                        $"{root.Name}\\{currentPath}"
                    );
                }

                // dispose old key if we opened it
                if (ownsCurrent)
                    current.Dispose();

                current = next;
                ownsCurrent = true;
            }

View on GitHub (pinned to 36acf585ae)