Why Does Rename-Item Fail on Forward Slashes?

When administering the Windows Registry via PowerShell, you might occasionally encounter registry keys that contain forward slash characters (/), commonly used to store URLs, UNC paths, or configuration strings. While PowerShell's Registry provider generally supports these keys during read or create operations, using Rename-Item often throws an error:

Rename-Item : The registry key at the specified path does not exist.

This occurs because PowerShell treats the forward slash (/) interchangeably with the backslash (\) as a path separator. When you pass a value containing / to the -NewName parameter, the underlying PowerShell Registry provider interprets the string as a nested relative path rather than a literal string name. Escaping with backticks (`/) will not prevent the provider from splitting the path.

Solution 1: Use Move-Item Instead of Rename-Item

Fortunately, the Move-Item cmdlet resolves destination paths differently than Rename-Item. If you supply the complete target path to the -Destination parameter, PowerShell correctly keeps the forward slashes intact as part of the key name.

$oldPath = "HKLM:\SOFTWARE\MySoftware\Path=//Server/UNC/path/goes/here"
$newPath = "HKLM:\SOFTWARE\MySoftware\Path=//Server/New/UNC/path/here"

# Use Move-Item to effectively rename the key
Move-Item -LiteralPath $oldPath -Destination $newPath

Tip: Always prefer using -LiteralPath instead of -Path to prevent PowerShell from misinterpreting characters like brackets, asterisks, or other wildcard symbols that often appear in path-like registry keys.

Solution 2: Use .NET Direct Registry APIs

If you prefer an approach that bypasses PowerShell's path parser completely, you can utilize the native .NET Microsoft.Win32.RegistryKey class. The Windows Registry API doesn't provide a direct "rename" method, but you can create the new key, migrate the values and subkeys recursively, and delete the original key.

Here is a robust reusable function to rename keys containing special characters:

function Rename-RegistryKeyLiteral {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [Microsoft.Win32.RegistryHive]$Hive,
        
        [Parameter(Mandatory = $true)]
        [string]$ParentPath,
        
        [Parameter(Mandatory = $true)]
        [string]$OldName,
        
        [Parameter(Mandatory = $true)]
        [string]$NewName
    )

    # Open the base hive and parent subkey with write access
    $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey($Hive, [Microsoft.Win32.RegistryView]::Default)
    $parent = $baseKey.OpenSubKey($ParentPath, $true)

    if (-not $parent) {
        throw "Parent path '$ParentPath' could not be found."
    }

    $sourceKey = $parent.OpenSubKey($OldName)
    if (-not $sourceKey) {
        $parent.Dispose()
        throw "Source key '$OldName' does not exist under '$ParentPath'."
    }

    # Create new subkey with the literal string name
    $destKey = $parent.CreateSubKey($NewName)

    # Helper scriptblock for recursive copying
    function Copy-KeyRecursively($src, $dst) {
        foreach ($valName in $src.GetValueNames()) {
            $val = $src.GetValue($valName, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
            $valKind = $src.GetValueKind($valName)
            $dst.SetValue($valName, $val, $valKind)
        }
        foreach ($subName in $src.GetSubKeyNames()) {
            $srcChild = $src.OpenSubKey($subName)
            $dstChild = $dst.CreateSubKey($subName)
            Copy-KeyRecursively $srcChild $dstChild
            $srcChild.Dispose()
            $dstChild.Dispose()
        }
    }

    try {
        Copy-KeyRecursively $sourceKey $destKey
        $sourceKey.Dispose()
        $destKey.Dispose()
        
        # Delete the old subkey tree
        $parent.DeleteSubKeyTree($OldName)
    }
finally {
        $parent.Dispose()
        $baseKey.Dispose()
    }
}

# Example usage:
Rename-RegistryKeyLiteral `
    -Hive LocalMachine `
    -ParentPath "SOFTWARE\MySoftware" `
    -OldName "Path=//Server/UNC/path/goes/here" `
    -NewName "Path=//Server/New/UNC/path/here"

Summary

  • Rename-Item fails because its -NewName parameter interprets / as a directory delimiter.
  • Move-Item with -LiteralPath and -Destination provides the quickest built-in workaround without writing external helper code.
  • .NET Registry APIs provide the most reliable and performant alternative when automating enterprise scripts where PowerShell provider path-mangling must be avoided.