{"record":{"id":"07c5b736796b8eb8","repo":"bitwarden/server","slug":"a-maximum-extension-length-is-required-when-extens","errorCode":null,"errorMessage":"A maximum extension length is required when extensions are allowed.","messagePattern":"A maximum extension length is required when extensions are allowed\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"bitwarden_license/src/Services/Pam/Services/AccessRuleWriteValidator.cs","lineNumber":39,"sourceCode":"        ICollectionRepository collectionRepository,\n        IAccessRuleValidator conditionsValidator)\n    {\n        _repository = repository;\n        _collectionRepository = collectionRepository;\n        _conditionsValidator = conditionsValidator;\n    }\n\n    public async Task<List<Guid>> ValidateAsync(Guid organizationId, AccessRule rule,\n        IEnumerable<Guid> collectionIds, Guid? existingRuleId = null)\n    {\n        if (string.IsNullOrWhiteSpace(rule.Name))\n        {\n            throw new BadRequestException(\"Name is required.\");\n        }\n\n        if (rule.AllowsExtensions && rule.MaxExtensionDurationSeconds is not > 0)\n        {\n            throw new BadRequestException(\"A maximum extension length is required when extensions are allowed.\");\n        }\n\n        var conditions = _conditionsValidator.Validate(rule.Conditions);\n        if (!conditions.IsValid)\n        {\n            throw new BadRequestException(conditions.Error!);\n        }\n\n        await ValidateNameIsUniqueAsync(organizationId, rule.Name, existingRuleId);\n\n        return await ValidateCollectionsAsync(organizationId, collectionIds, existingRuleId);\n    }\n\n    private async Task ValidateNameIsUniqueAsync(Guid organizationId, string name, Guid? existingRuleId)\n    {\n        var siblings = await _repository.GetManyByOrganizationIdAsync(organizationId);\n        if (siblings.Any(r => r.Id != existingRuleId && string.Equals(r.Name, name, StringComparison.OrdinalIgnoreCase)))\n        {","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/bitwarden_license/src/Services/Pam/Services/AccessRuleWriteValidator.cs#L21-L57","documentation":"Thrown as a BadRequestException (HTTP 400) by AccessRuleWriteValidator.ValidateAsync when AllowsExtensions is true but MaxExtensionDurationSeconds is not a positive integer. When an access rule permits time extensions, a maximum duration must be set to bound how long an extension can last.","triggerScenarios":"Creating or updating a PAM AccessRule with allowsExtensions=true but maxExtensionDurationSeconds omitted, zero, or negative. The validator uses the pattern 'is not > 0', so null, 0, and negative values all trigger it.","commonSituations":"Client enables extensions in the UI but forgets to set a max duration. API request omits maxExtensionDurationSeconds because it was assumed optional. A migration script copies rules but drops the duration field.","solutions":["Set maxExtensionDurationSeconds to a positive integer (e.g., 3600 for 1 hour) when allowsExtensions is true.","If extensions are not needed, set allowsExtensions to false.","Add a client-side co-validation: if extensions are enabled, require a max duration input."],"exampleFix":"// before\n//   { \"name\": \"Rule\", \"allowsExtensions\": true, \"maxExtensionDurationSeconds\": 0 }\n// after\n//   { \"name\": \"Rule\", \"allowsExtensions\": true, \"maxExtensionDurationSeconds\": 3600 }","handlingStrategy":"validation","validationCode":"if (rule.AllowsExtensions && !(rule.MaxExtensionDurationSeconds > 0))\n    throw new InvalidOperationException(\"maxExtensionDurationSeconds must be > 0 when extensions are allowed\");\n// only then submit to API","typeGuard":null,"tryCatchPattern":"try { await pamClient.CreateAccessRuleAsync(orgId, rule); }\ncatch (HttpRequestException ex) when (ex.Message.Contains(\"maximum extension length\"))\n{ /* set maxExtensionDurationSeconds or disable extensions, then retry */ }","preventionTips":["Co-validate in the UI: when extensions are enabled, require a max-duration input.","Default maxExtensionDurationSeconds to a sensible value when allowsExtensions is toggled on.","Add a client-side rule: allowsExtensions=true implies maxExtensionDurationSeconds > 0."],"tags":["pam","validation","access-rules","http-400","conditional-required"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}