{"record":{"id":"3b53a55bd946e826","repo":"CoplayDev/unity-mcp","slug":"credwrite-failed-win32-getlastwin32error","errorCode":null,"errorMessage":"CredWrite failed (Win32 {GetLastWin32Error})","messagePattern":"CredWrite failed \\(Win32 (.+?)\\)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"MCPForUnity/Editor/Security/SecureKeyStore/WindowsCredentialKeyStore.cs","lineNumber":90,"sourceCode":"            if (string.IsNullOrEmpty(providerId)) return;\n            if (string.IsNullOrEmpty(apiKey)) { Delete(providerId); return; }\n            byte[] blob = Encoding.UTF8.GetBytes(apiKey);\n            IntPtr blobPtr = Marshal.AllocHGlobal(blob.Length);\n            try\n            {\n                Marshal.Copy(blob, 0, blobPtr, blob.Length);\n                var cred = new CREDENTIAL\n                {\n                    Type = CRED_TYPE_GENERIC,\n                    TargetName = Target(providerId),\n                    CredentialBlobSize = blob.Length,\n                    CredentialBlob = blobPtr,\n                    Persist = CRED_PERSIST_LOCAL_MACHINE,\n                    UserName = providerId,\n                };\n                if (!CredWrite(ref cred, 0))\n                {\n                    throw new InvalidOperationException(\n                        \"CredWrite failed (Win32 \" + Marshal.GetLastWin32Error() + \")\");\n                }\n            }\n            finally\n            {\n                Marshal.FreeHGlobal(blobPtr);\n            }\n        }\n\n        public void Delete(string providerId)\n        {\n            if (string.IsNullOrEmpty(providerId)) return;\n            CredDelete(Target(providerId), CRED_TYPE_GENERIC, 0);\n        }\n    }\n}\n","sourceCodeStart":72,"sourceCodeEnd":107,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/MCPForUnity/Editor/Security/SecureKeyStore/WindowsCredentialKeyStore.cs#L72-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Best-effort pre-check that the credential target is writable is not reliable on Windows;\n// instead validate inputs and rely on catch + error-code mapping.\nif (string.IsNullOrEmpty(providerId))\n    throw new ArgumentException(\"providerId required\", nameof(providerId));","typeGuard":null,"tryCatchPattern":"try { store.Set(providerId, blob); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"CredWrite failed\"))\n{\n    int winErr = ExtractWin32Error(ex.Message); // parse the trailing code\n    if (winErr == 5 /*ERROR_ACCESS_DENIED*/)\n        throw new UnauthorizedAccessException(\"Credential write denied; run with sufficient privileges.\", ex);\n    throw;\n}","preventionTips":["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)."],"tags":["security","credentials","windows","pinvoke","secrets"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}