{"record":{"id":"42649a6076d213b6","repo":"JeffreySu/WeiXinMPSDK","slug":"corpsecret","errorCode":null,"errorMessage":"凭据提供器返回了空 CorpSecret。","messagePattern":"凭据提供器返回了空 CorpSecret。","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/Containers/AccessTokenContainer.cs","lineNumber":402,"sourceCode":"                throw new ArgumentException(\"registrationKey 不能为空。\", nameof(registrationKey));\n            }\n\n            if (string.IsNullOrWhiteSpace(corpId))\n            {\n                throw new ArgumentException(\"CorpId 不能为空。\", nameof(corpId));\n            }\n\n            if (credentialProvider == null)\n            {\n                throw new ArgumentNullException(nameof(credentialProvider));\n            }\n\n            async Task<AccessTokenBag> RegisterCoreAsync(CancellationToken token)\n            {\n                var secret = await credentialProvider.GetSecretAsync(registrationKey, token).ConfigureAwait(false);\n                if (string.IsNullOrWhiteSpace(secret))\n                {\n                    throw new InvalidOperationException(\"凭据提供器返回了空 CorpSecret。\");\n                }\n\n                var bag = new AccessTokenBag\n                {\n                    Name = name,\n                    CorpId = corpId,\n                    CorpSecret = secret,\n                    ExpireTime = DateTimeOffset.MinValue,\n                    AccessTokenResult = new AccessTokenResult()\n                };\n                await UpdateAsync(registrationKey, bag, null).ConfigureAwait(false);\n                return bag;\n            }\n\n            SetRegistrationCallback(registrationKey, () => RegisterCoreAsync(CancellationToken.None));\n            cancellationToken.ThrowIfCancellationRequested();\n            await RegisterCoreAsync(cancellationToken).ConfigureAwait(false);\n        }","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/Containers/AccessTokenContainer.cs#L384-L420","documentation":"Inside RegisterWithCredentialProviderAsync, RegisterCoreAsync throws InvalidOperationException(\"凭据提供器返回了空 CorpSecret。\") when the credential provider's GetSecretAsync returns null/empty/whitespace. Unlike the argument guards this is a runtime failure: arguments were valid but the provider could not supply the secret, so the access token bag cannot be created. It indicates a problem in the credential provider implementation or its backing store.","triggerScenarios":"The registered IWeixinCredentialProvider.GetSecretAsync(registrationKey) returns null or \"\" — e.g. key not found in the provider's store, secret removed from config/secret manager, provider returning default(null).","commonSituations":"registrationKey not present in the secret store (typo, wrong environment); Azure Key Vault / database entry deleted; provider reading from config section that is missing in the deployed environment.","solutions":["Verify GetSecretAsync returns the real corp secret for that registrationKey; log the key (not the secret) inside the provider when it misses.","Fix the secret store: add the entry for the registrationKey in the correct environment (dev/prod).","Correct any key mismatch between what is passed to RegisterWithCredentialProviderAsync and what the provider stores.","If the provider intentionally has no secret for a key, skip registration instead of calling it with that key."],"exampleFix":"// before\npublic Task<string> GetSecretAsync(string key, CancellationToken ct) => _secrets.TryGetValue(key, out var s) ? Task.FromResult(s) : Task.FromResult<string>(null);\n// after\npublic Task<string> GetSecretAsync(string key, CancellationToken ct)\n{\n    if (!_secrets.TryGetValue(key, out var s) || string.IsNullOrWhiteSpace(s))\n        throw new InvalidOperationException($\"No secret configured for registrationKey '{key}'.\");\n    return Task.FromResult(s);\n}","handlingStrategy":"try-catch","validationCode":"// Inside your IWeixinCredentialProvider implementation:\nvar secret = await _store.GetAsync(registrationKey);\nif (string.IsNullOrWhiteSpace(secret))\n    throw new InvalidOperationException($\"Secret store has no entry for '{registrationKey}'.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await AccessTokenContainer.RegisterWithCredentialProviderAsync(regKey, corpId, provider);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"CorpSecret\"))\n{\n    log.LogCritical(ex, \"Credential provider returned empty secret for key {Key}; check secret store.\", regKey);\n    throw;\n}","preventionTips":["Have credential providers throw a descriptive error instead of returning null when a key is missing.","Keep dev/prod secret stores in sync and verify keys during deployment smoke tests.","Log which registrationKey failed (never the secret value) to speed up diagnosis."],"tags":["credential-provider","empty-secret","runtime"],"backgroundTag":"missing-credentials","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}