a-b-street/abstreet · error

CityName::new( , ) has a country code that isn't two letters

Error message

CityName::new({}, {}) has a country code that isn't two letters

What it means

CityName::new validates that the country argument is exactly two characters (an ISO 3166-1 alpha-2 code, lowercase, or 'zz' for imaginary cities) because the country code is used as a directory name in path construction. Passing a longer (or shorter) string panics with this message.

Solutions

  1. Pass a valid two-letter ISO 3166-1 alpha-2 lowercase code (e.g. 'us', 'gb', 'zz' for test cities)
  2. Use CityName::parse('us/seattle') or the built-in constructors like CityName::seattle() / MapName::seattle(map) instead of hand-building names
  3. Rename any non-two-letter directories under data/system/ or importer/config/ so directory scans don't produce invalid codes
  4. Validate the country string before calling: check country.len() == 2 and use CityName::parse or your own Result-returning wrapper for external input

Example fix

// before
let city = CityName::new("usa", "seattle"); // panics
// after
let city = CityName::new("us", "seattle");
// or validated:
assert_eq!(country.len(), 2);
let city = CityName::parse(&format!("{}/{}", country, city_name))?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_country_code(country: &str) -> bool {
    country.len() == 2 && country.chars().all(|c| c.is_ascii_lowercase())
}
// call only if valid_country_code(country) else handle/reprompt

Type guard

fn as_country_code(s: &str) -> Option<&str> {
    (s.len() == 2 && s.chars().all(|c| c.is_ascii_lowercase())).then_some(s)
}

Try / catch

let city = std::panic::catch_unwind(|| CityName::new(country, city))
    .map_err(|_| anyhow::anyhow!("country code '{}' must be two letters", country))?;

Prevention

When it happens

Trigger: Calling CityName::new(country, city), MapName::new(country, city, map), or CityName::parse with a country string whose byte length is not 2 — e.g. 'usa', 'US' is fine length-wise but 'USA', 'United States', or an empty string will panic. list_all_cities_locally / list_all_cities_from_importer_config also hit this if a directory under data/system/ or importer/config/ is not a two-letter code.

Common situations: Hardcoding 'usa' instead of 'us', using full country names, creating a stray misnamed directory (e.g. data/system/misc/) that gets scanned as a country, or user-supplied input passed straight into CityName::new.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/7916fe0c851b3e04. Report an issue: GitHub.

Appendix: source

Thrown at abstio/src/abst_paths.rs:83

        format!("{dir}/{p}")
    }
}

/// A single city is identified using this.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct CityName {
    /// A two letter lowercase country code, from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2.
    /// To represent imaginary/test cities, use the code `zz`.
    pub country: String,
    /// The name of the city, in filename-friendly form -- for example, "tel_aviv".
    pub city: String,
}

impl CityName {
    /// Create a CityName from a country code and city.
    pub fn new(country: &str, city: &str) -> CityName {
        if country.len() != 2 {
            panic!(
                "CityName::new({}, {}) has a country code that isn't two letters",
                country, city
            );
        }
        CityName {
            country: country.to_string(),
            city: city.to_string(),
        }
    }

    /// Convenient constructor for the main city of the game.
    pub fn seattle() -> CityName {
        CityName::new("us", "seattle")
    }

    /// Returns all city names available locally.
    fn list_all_cities_locally() -> Vec<CityName> {
        let mut cities = Vec::new();

View on GitHub (pinned to 0964f29315)