---
title: "Diagnose an undetermined address country in C#"
url: https://recursiveai.org/onecontact/cookbook/diagnose-ambiguous-postal-address/
description: "Inspect a country-ambiguous postal address in C#: retain partial fields, read stable reason IDs and retry with an explicit country using OneContact."
version: "1.0.0"
---

# Diagnose an undetermined address country in C#

**OneContact** is a free, open-source contact data library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies, no network access.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneContact) · [GitHub](https://github.com/recursive-ai-oss/onecontact-csharp) · [Docs](https://recursiveai.org/onecontact/docs/) · [Download example (.zip)](https://recursiveai.org/onecontact/cookbook/diagnose-ambiguous-postal-address/diagnose-ambiguous-postal-address.zip)

An address parser can recover fields without having enough evidence to choose a country.

```shell
dotnet add package RecursiveAI.OneContact
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneContact;

string text =
    "Unter den Linden 77, 10117 Berlin";
var status = PostalAddress.TryParse(
    text.AsSpan(), out var partial, out _);
Console.WriteLine(status);
Console.WriteLine(partial.Country);
Console.WriteLine(partial.Flags);
var address = PostalAddress.Parse(
    text, Region.Parse("DE"));
Console.WriteLine(address);
```

```text
Done
ZZ
CountryUndetermined
Unter den Linden 77, 10117 BERLIN
```

Postcodes with the five-digit shape of `10117` occur in several countries, so this text alone does not establish the country.

Unzip [the example](https://recursiveai.org/onecontact/cookbook/diagnose-ambiguous-postal-address/diagnose-ambiguous-postal-address.zip), then `dotnet run`.

## Full program

This Berlin address has a postcode shape shared by multiple countries. The retry uses Germany because the caller explicitly knows that country.

```csharp
using RecursiveAI.OneContact;

string text = "Unter den Linden 77, 10117 Berlin";
var status = PostalAddress.TryParse(text.AsSpan(), out var partial, out var detail);
Console.WriteLine($"{status}: {partial.Country}, {partial.Flags}");
Console.WriteLine($"Locality: {partial.Locality}; postcode: {partial.PostalCode}");
foreach (var issue in detail.Issues)
    Console.WriteLine(issue.Reason.Id);
Console.WriteLine($"Happy path: {PostalAddress.TryParse(text, out _)}");
var address = PostalAddress.Parse(text, Region.Parse("DE"));
Console.WriteLine($"{address.Country}: {address.Canonical}");
```

```text
Done: ZZ, CountryUndetermined
Locality: Berlin; postcode: 10117
address.country-undetermined
Happy path: False
DE: Unter den Linden 77, 10117 BERLIN
```

The simple Boolean `TryParse` overload (the happy path) returns false for an undetermined country; `Parse` throws `ContactParseException` with `address.country-undetermined`.

## Variations

### Catch the stable reason from Parse

```csharp
using RecursiveAI.OneContact;

try
{
    PostalAddress.Parse("Unter den Linden 77, 10117 Berlin");
}
catch (ContactParseException error)
{
    Console.WriteLine(error.Reason.Id);
}
```

```text
address.country-undetermined
```

### Inspect disputed fields

```csharp
using RecursiveAI.OneContact;

var status = PostalAddress.TryParse(
    "Muster 12-14, 10115 Berlin".AsSpan(), out var partial, out var detail);
Console.WriteLine($"{status}: {partial.Flags}");
foreach (var issue in detail.Issues)
    Console.WriteLine(issue.Reason.Id);
```

```text
Done: CountryUndetermined, FieldsDisputed
address.country-undetermined
address.fields-disputed
```

Here the country candidates disagree about the house/unit interpretation of `12-14`. The partial result leaves disputed elements out instead of choosing one.

## Related recipes

- [Parse a postal address](https://recursiveai.org/onecontact/cookbook/parse-postal-address/)
- [Format a postal address](https://recursiveai.org/onecontact/cookbook/format-postal-address/)
- [Deduplicate postal addresses](https://recursiveai.org/onecontact/cookbook/deduplicate-postal-addresses/)

## The short answer

Use the status-returning overload `PostalAddress.TryParse(span, out value, out detail)` when you need a partial address reading. Check both the status and the returned flags. `Done` can coexist with `CountryUndetermined`; it does not mean the country has been established. Retry with an explicit `Region` only when you have that context independently.

Use the simpler Boolean `TryParse` overload when your application needs a usable country-specific address or a refusal. It rejects the same ambiguous input rather than returning a partial value as a successful address.

## Why is my address parser’s country undetermined?

A postcode is not always a country identifier. Several countries share four-digit or five-digit patterns. Without a country hint or distinguishing evidence in the text, selecting the first country that accepts a pattern would turn missing information into an unsupported assumption.

OneContact’s [country-undetermined rules](https://recursiveai.org/onecontact/docs/address-country-undetermined/) try the candidate country readings. When the candidates agree on the fields, the low-level result retains those fields and leaves `Country` as `Region.Unknown`, printed as `ZZ`. It sets `AddressFacts.CountryUndetermined` and carries the warning reason `address.country-undetermined` in the parse detail.

That is why the full program prints `Done` and an unknown country together. It has recovered Berlin and the postcode, but it has not established Germany. The happy-path Boolean overload returns false, so a checkout form does not accidentally accept a countryless result as a complete shipping address.

The undetermined canonical text stays as written apart from trimming and joining input parts; no country’s postal style is applied. Coverage is structural and validation reports the missing country. A partial key can begin with `zz`, so keep partial readings separate from accepted country-specific addresses in a production import.

## How do I parse an address with an explicit country in C#?

Pass a known `Region` as the second argument. The retry in the full program uses `Region.Parse("DE")`; the hinted result prints `DE` and Germany’s canonical rendering. The country must come from something you know, such as a user-selected country or a trusted field in the source record, not from the ambiguous postcode itself.

For a batch import, separate records needing country confirmation from records that failed for other reasons. A hint can select the country’s parser, but it does not establish delivery or resolve every malformed address. Countries without free-text support can still refuse the input. See [the overload contract](https://recursiveai.org/onecontact/docs/api-shapes/) for the distinction between the happy path and low-level engine.

The disputed-field variation is a further case: all candidates agree on the postal code but disagree on some address elements. The engine retains only agreed elements, sets `FieldsDisputed` with `CountryUndetermined`, and adds `address.fields-disputed`. Do not fill those missing elements from a candidate you arbitrarily prefer.

For logging, use `issue.Reason.Id`, or the caught exception’s `Reason.Id`, rather than parsing a human-readable message. Those IDs are the documented stable wire text. If you switch on the reason enumeration instead, retain a fallback for new members. Keep the original input alongside the partial result so a person can resolve the country and rerun parsing without reconstructing the source from incomplete fields.
