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
- Read the Win32 error code in the message and map it (e.g. ERROR_ACCESS_DENIED, ERROR_BAD_USERNAME, ERROR_NOT_ENOUGH_MEMORY).
- Run with sufficient privileges or change the persist scope if policy allows.
- Clear stale credentials for the target name via Credential Manager (control keymgr.dll) and retry.
- 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
- Run under an account with Credential Manager write privileges.
- Clear stale credentials for the target name before writing.
- Map the Win32 error code in the message to choose recovery (access-denied vs quota vs corrupt).
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
- provider returned a disallowed file type '.{ext}'
- Unsafe zip entry rejected: {name}
- Unsafe zip entry escapes destination: {name}
- image_path must point to a file under the project's Assets f
- {context}: refusing to send credentials to an unexpected hos
AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13).
Data as JSON: /api/errors/3b53a55bd946e826.
Report an issue: GitHub.