CoplayDev/unity-mcp · error · InvalidOperationException

CredWrite failed (Win32 {GetLastWin32Error})

Error message

CredWrite failed (Win32 {GetLastWin32Error})

What it means

WindowsCredentialKeyStore calls the Win32 CredWrite API to store a secret blob in the Windows Credential Manager under CRED_PERSIST_LOCAL_MACHINE. If CredWrite returns false, it throws InvalidOperationException including Marshal.GetLastWin32Error(). This is a thin P/Invoke wrapper surfacing the native failure code.

Source

Thrown at MCPForUnity/Editor/Security/SecureKeyStore/WindowsCredentialKeyStore.cs:90

            if (string.IsNullOrEmpty(providerId)) return;
            if (string.IsNullOrEmpty(apiKey)) { Delete(providerId); return; }
            byte[] blob = Encoding.UTF8.GetBytes(apiKey);
            IntPtr blobPtr = Marshal.AllocHGlobal(blob.Length);
            try
            {
                Marshal.Copy(blob, 0, blobPtr, blob.Length);
                var cred = new CREDENTIAL
                {
                    Type = CRED_TYPE_GENERIC,
                    TargetName = Target(providerId),
                    CredentialBlobSize = blob.Length,
                    CredentialBlob = blobPtr,
                    Persist = CRED_PERSIST_LOCAL_MACHINE,
                    UserName = providerId,
                };
                if (!CredWrite(ref cred, 0))
                {
                    throw new InvalidOperationException(
                        "CredWrite failed (Win32 " + Marshal.GetLastWin32Error() + ")");
                }
            }
            finally
            {
                Marshal.FreeHGlobal(blobPtr);
            }
        }

        public void Delete(string providerId)
        {
            if (string.IsNullOrEmpty(providerId)) return;
            CredDelete(Target(providerId), CRED_TYPE_GENERIC, 0);
        }
    }
}

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Read the Win32 error code in the message and map it (e.g. ERROR_ACCESS_DENIED, ERROR_BAD_USERNAME, ERROR_NOT_ENOUGH_MEMORY).
  2. Run with sufficient privileges or change the persist scope if policy allows.
  3. Clear stale credentials for the target name via Credential Manager (control keymgr.dll) and retry.
  4. Verify the Credential Manager service is running.
Defensive patterns

Strategy: try-catch

Validate before calling

// Best-effort pre-check that the credential target is writable is not reliable on Windows;
// instead validate inputs and rely on catch + error-code mapping.
if (string.IsNullOrEmpty(providerId))
    throw new ArgumentException("providerId required", nameof(providerId));

Try / catch

try { store.Set(providerId, blob); }
catch (InvalidOperationException ex) when (ex.Message.Contains("CredWrite failed"))
{
    int winErr = ExtractWin32Error(ex.Message); // parse the trailing code
    if (winErr == 5 /*ERROR_ACCESS_DENIED*/)
        throw new UnauthorizedAccessException("Credential write denied; run with sufficient privileges.", ex);
    throw;
}

Prevention

When it happens

Trigger: The Credential Manager service is unavailable, the store quota is exceeded, an ACL/permission issue blocks the target, or the store is corrupt. The blob is marshaled and freed in a finally block, so the throw happens after the native call returns false.

Common situations: Group policy restricting credential writes; running under a low-privilege account without access to CRED_PERSIST_LOCAL_MACHINE; credential store full of stale entries; a corrupt credential target.

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/3b53a55bd946e826. Report an issue: GitHub.