[BUG] FormatException from XmlConvert.ToTimeSpan while enumerating DPP backup jobs and policies
Library name and version
Azure.ResourceManager.DataProtectionBackup 1.7.1 (also reproduced on 1.7.0)
Describe the bug
Two distinct enumeration paths in the DPP (Data Protection Backup) management library throw System.FormatException from XmlConvert.ToTimeSpan when the service returns ISO 8601 duration values that the SDK's strict parser rejects (e.g. week designators like P4W, fractional seconds, or service-specific extensions). Once the exception is raised mid-pagination, the caller cannot retrieve any further items from the same listing — including items whose duration fields are valid.
Two affected code paths:
1. Listing jobs — DataProtectionBackupVaultResource.GetDataProtectionBackupJobs().GetAllAsync()
System.FormatException
at System.Xml.XmlConvert.ToTimeSpan(String s)
at Azure.ResourceManager.DataProtectionBackup.TypeFormatters.ParseTimeSpan(String value, String format)
at Azure.ResourceManager.DataProtectionBackup.ModelSerializationExtensions.GetTimeSpan(JsonElement element, String format)
at Azure.ResourceManager.DataProtectionBackup.Models.DataProtectionBackupJobProperties.DeserializeDataProtectionBackupJobProperties(JsonElement element, ModelReaderWriterOptions options)
at Azure.ResourceManager.DataProtectionBackup.DataProtectionBackupJobData.DeserializeDataProtectionBackupJobData(JsonElement element, ModelReaderWriterOptions options)
at Azure.ResourceManager.DataProtectionBackup.Models.AzureBackupJobResourceList.DeserializeAzureBackupJobResourceList(JsonElement element, ModelReaderWriterOptions options)
at Azure.ResourceManager.DataProtectionBackup.Models.AzureBackupJobResourceList.FromResponse(Response response)
at Azure.ResourceManager.DataProtectionBackup.AzureBackupJobResourcesGetAllAsyncCollectionResultOfT.AsPages(...)+MoveNext()
2. Listing / getting backup policies — DataProtectionBackupVaultResource.GetDataProtectionBackupPolicies().GetAllAsync() and .GetAsync(name)
The same parser is invoked when deserializing DataProtectionBackupAbsoluteDeleteSetting.Duration inside policy retention rules:
System.FormatException
at System.Xml.XmlConvert.ToTimeSpan(String s)
at Azure.ResourceManager.DataProtectionBackup.TypeFormatters.ParseTimeSpan(String value, String format)
at Azure.ResourceManager.DataProtectionBackup.ModelSerializationExtensions.GetTimeSpan(JsonElement element, String format)
at Azure.ResourceManager.DataProtectionBackup.Models.DataProtectionBackupAbsoluteDeleteSetting.DeserializeDataProtectionBackupAbsoluteDeleteSetting(JsonElement element, ModelReaderWriterOptions options)
at Azure.ResourceManager.DataProtectionBackup.Models.DataProtectionBaseBackupPolicy.DeserializeDataProtectionBaseBackupPolicy(JsonElement element, ModelReaderWriterOptions options)
at Azure.ResourceManager.DataProtectionBackup.DataProtectionBackupPolicyData.DeserializeDataProtectionBackupPolicyData(JsonElement element, ModelReaderWriterOptions options)
at Azure.ResourceManager.DataProtectionBackup.DataProtectionBackupPolicyResource.GetAsync(CancellationToken cancellationToken)
Single-item GetAsync(policyName) fails outright; GetAllAsync() cuts the listing short.
Expected behavior
GetAllAsync() (jobs and policies) and GetAsync(name) (policies) should successfully materialize results even when the service returns duration strings the strict ISO 8601 parser doesn't accept — either by (a) using a more lenient parser, (b) round-tripping unknown values into the RawData bag instead of throwing, or (c) replacing the unparseable value with null / TimeSpan.Zero and continuing.
Actual behavior
The first malformed duration aborts deserialization and surfaces a raw FormatException to the caller. There is no error envelope, no RequestFailedException, and no way to skip just the offending item — pagination is broken at that page.
Reproduction Steps
var armClient = new ArmClient(new DefaultAzureCredential());
var vault = armClient.GetDataProtectionBackupVaultResource(
DataProtectionBackupVaultResource.CreateResourceIdentifier(subId, rg, vaultName));
// Path 1 - jobs
await foreach (var job in vault.GetDataProtectionBackupJobs().GetAllAsync())
{
Console.WriteLine(job.Data.Name);
}
// Path 2 - policies
await foreach (var p in vault.GetDataProtectionBackupPolicies().GetAllAsync())
{
Console.WriteLine(p.Data.Name);
}
var single = await vault.GetDataProtectionBackupPolicies().GetAsync("policyWithLongRetention");
The exception is intermittent — it depends on whether the vault contains a job/policy whose duration field is in a format XmlConvert.ToTimeSpan rejects.
Impact
Related
Suggested fix
Either:
- Use a more lenient parser in
TypeFormatters.ParseTimeSpan (handle week designators / fractional seconds), or
- Wrap
XmlConvert.ToTimeSpan in a try/catch in the generated Deserialize* methods and surface unknown durations as null / preserve in RawData, or
- Coordinate with the Azure Backup service team to validate that all returned duration strings conform to the SDK's accepted ISO 8601 subset.
Option (2) is the safest backwards-compatible patch.
Environment
- OS: Windows 11 (also seen on Linux containers)
- .NET: 10.0
Azure.ResourceManager.DataProtectionBackup: 1.7.1
Azure.ResourceManager: 1.15.0
[BUG] FormatExceptionfromXmlConvert.ToTimeSpanwhile enumerating DPP backup jobs and policiesLibrary name and version
Azure.ResourceManager.DataProtectionBackup1.7.1 (also reproduced on 1.7.0)Describe the bug
Two distinct enumeration paths in the DPP (Data Protection Backup) management library throw
System.FormatExceptionfromXmlConvert.ToTimeSpanwhen the service returns ISO 8601 duration values that the SDK's strict parser rejects (e.g. week designators likeP4W, fractional seconds, or service-specific extensions). Once the exception is raised mid-pagination, the caller cannot retrieve any further items from the same listing — including items whose duration fields are valid.Two affected code paths:
1. Listing jobs —
DataProtectionBackupVaultResource.GetDataProtectionBackupJobs().GetAllAsync()2. Listing / getting backup policies —
DataProtectionBackupVaultResource.GetDataProtectionBackupPolicies().GetAllAsync()and.GetAsync(name)The same parser is invoked when deserializing
DataProtectionBackupAbsoluteDeleteSetting.Durationinside policy retention rules:Single-item
GetAsync(policyName)fails outright;GetAllAsync()cuts the listing short.Expected behavior
GetAllAsync()(jobs and policies) andGetAsync(name)(policies) should successfully materialize results even when the service returns duration strings the strict ISO 8601 parser doesn't accept — either by (a) using a more lenient parser, (b) round-tripping unknown values into theRawDatabag instead of throwing, or (c) replacing the unparseable value withnull/TimeSpan.Zeroand continuing.Actual behavior
The first malformed duration aborts deserialization and surfaces a raw
FormatExceptionto the caller. There is no error envelope, noRequestFailedException, and no way to skip just the offending item — pagination is broken at that page.Reproduction Steps
The exception is intermittent — it depends on whether the vault contains a job/policy whose duration field is in a format
XmlConvert.ToTimeSpanrejects.Impact
Azure.ResourceManager.DataProtectionBackupjob/policy enumeration.FormatExceptionper-item and return partial results, but this silently drops policies/jobs from the listing — see [AzureBackup] Surface RequestFailedException on dual-backend vault list failure; tolerate DPP policy FormatException microsoft/mcp#2805 (tools/Azure.Mcp.Tools.AzureBackup/src/Services/DppBackupOperations.cs).Related
DataProtectionOperationJobExtendedInfo.Deserialize(Guid.Parseinstead ofXmlConvert.ToTimeSpan). Fixed in [DataProtectionBackup]Fix for issue 36360 #36901 (2023) but the underlying brittleness in the generated deserialization code remains.Suggested fix
Either:
TypeFormatters.ParseTimeSpan(handle week designators / fractional seconds), orXmlConvert.ToTimeSpanin a try/catch in the generatedDeserialize*methods and surface unknown durations asnull/ preserve inRawData, orOption (2) is the safest backwards-compatible patch.
Environment
Azure.ResourceManager.DataProtectionBackup: 1.7.1Azure.ResourceManager: 1.15.0