{"record":{"id":"6ef9467b5b080517","repo":"OrchardCMS/OrchardCore","slug":"code-cannot-be-null-or-empty","errorCode":null,"errorMessage":"code cannot be null or empty.","messagePattern":"code cannot be null or empty\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.Users.Core/Services/UserStore.cs","lineNumber":848,"sourceCode":"\n    #region IUserTwoFactorRecoveryCodeStore\n    public Task ReplaceCodesAsync(IUser user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(user);\n        ArgumentNullException.ThrowIfNull(recoveryCodes);\n\n        var mergedCodes = string.Join(\";\", recoveryCodes);\n\n        return SetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken);\n    }\n\n    public async Task<bool> RedeemCodeAsync(IUser user, string code, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(user);\n\n        if (string.IsNullOrWhiteSpace(code))\n        {\n            throw new ArgumentException($\"{nameof(code)} cannot be null or empty.\");\n        }\n\n        var mergedCodes = (await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken)) ?? string.Empty;\n        var splitCodes = mergedCodes.Split(';');\n        if (splitCodes.Contains(code))\n        {\n            var updatedCodes = new List<string>(splitCodes.Where(s => s != code));\n            await ReplaceCodesAsync(user, updatedCodes, cancellationToken);\n\n            return true;\n        }\n\n        return false;\n    }\n\n    public async Task<int> CountCodesAsync(IUser user, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(user);","sourceCodeStart":830,"sourceCodeEnd":866,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.Users.Core/Services/UserStore.cs#L830-L866","documentation":"Thrown by UserStore.RedeemCodeAsync when the recovery code argument is null or whitespace. Two-factor recovery codes are stored merged with ';' separators and matched exactly, so a blank code can never be valid and is rejected up front with ArgumentException.","triggerScenarios":"Calling RedeemCodeAsync (via UserManager.RedeemTwoFactorRecoveryCodeAsync) with an empty or whitespace-only code from a form; a user submitting the recovery-code field blank.","commonSituations":"Two-factor login pages without client-side validation; API clients sending empty code fields; automation scripts with unset variables.","solutions":["Validate the code is non-empty before calling (string.IsNullOrWhiteSpace check) and return a validation message instead.","Require the code field in the login form/API model ([Required], NotEmpty).","Catch ArgumentException and convert it to a failed-login response.","Trim and normalize user input before comparison."],"exampleFix":"// before\nvar ok = await _userManager.RedeemTwoFactorRecoveryCodeAsync(user, code);\n// after\nif (string.IsNullOrWhiteSpace(code)) { ModelState.AddModelError(nameof(code), \"A recovery code is required.\"); return View(); }\nvar ok = await _userManager.RedeemTwoFactorRecoveryCodeAsync(user, code.Trim());","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(code)) return false; // or add model error before calling","typeGuard":"bool IsPlausibleRecoveryCode(string code) => !string.IsNullOrWhiteSpace(code) && code.Length >= 8;","tryCatchPattern":"try { var ok = await _userManager.RedeemTwoFactorRecoveryCodeAsync(user, code); }\ncatch (ArgumentException) { return SignInResult.Failed; }","preventionTips":["Mark the recovery-code input [Required] and non-empty","Trim user input before redeeming","Add client-side validation on the two-factor login form","Never call redeem APIs with unset script variables"],"tags":["users","two-factor","recovery-codes"],"backgroundTag":"missing-required-argument","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}