a-b-street/abstreet · error
Bad CityName
Error message
Bad CityName {} What it means
CityName::parse expects a string of the form "country/city" where the country part is exactly two characters (like "gb/london"). Anything else — wrong number of slash-separated parts or a country code not two chars long — is rejected with "Bad CityName".
Solutions
- Pass a properly formatted "cc/city" string with a two-letter country code.
- If the input is a full path or URL, extract the last two components (e.g. take the two path segments after the data/system prefix) before parsing.
- Use CityName::new directly when you already have separate country and city components.
Example fix
// before
let name = CityName::parse("gb/camden/london").ok();
// after
let name = CityName::new("gb".to_string(), "london".to_string()); Defensive patterns
Strategy: validation
Validate before calling
fn is_valid_city_name(s: &str) -> bool {
let parts: Vec<&str> = s.split('/').collect();
parts.len() == 2 && parts[0].len() == 2
} Try / catch
match CityName::parse(input) {
Ok(name) => name,
Err(err) => { log::warn!("{}", err); CityName::new("gb".into(), "london".into()) }
} Prevention
- Prefer CityName::new(country, city) over parsing free-form strings.
- Validate user-supplied city strings before calling parse.
- Extract only the last two path segments when deriving a CityName from a full path.
When it happens
Trigger: Calling CityName::parse with strings missing a slash ("london"), with extra segments ("gb/camden/london"), with a non-2-letter country code ("gbr/london"), or with an empty string.
Common situations: Hand-editing city paths in configs or CLI args; on web, deriving the city name from a URL path that includes more segments; older data layouts with different nesting.
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
- CityName::new( , ) has a country code that isn't two letters
- error loading svg
- render_line( )
- curvey( )
- Dropdown has default_value , but none of the choices match…
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/82dcc5edbcfe8739.
Report an issue: GitHub.
Appendix: source
Thrown at abstio/src/abst_paths.rs:164
}
}
cities
}
/// Returns all maps in a city based on importer config.
pub fn list_all_maps_in_city_from_importer_config(&self) -> Vec<MapName> {
crate::list_dir(format!("importer/config/{}/{}", self.country, self.city))
.into_iter()
.filter(|path| path.ends_with(".geojson"))
.map(|path| MapName::from_city(self, &basename(path)))
.collect()
}
/// Parses a CityName from something like "gb/london"; the inverse of `to_path`.
pub fn parse(x: &str) -> Result<CityName> {
let parts = x.split('/').collect::<Vec<_>>();
if parts.len() != 2 || parts[0].len() != 2 {
bail!("Bad CityName {}", x);
}
Ok(CityName::new(parts[0], parts[1]))
}
/// Expresses the city as a path, like "gb/london"; the inverse of `parse`.
pub fn to_path(&self) -> String {
format!("{}/{}", self.country, self.city)
}
/// Stringify the city name for debug messages. Don't implement `std::fmt::Display`, to force
/// callers to explicitly opt into this description, which could change.
pub fn describe(&self) -> String {
format!("{} ({})", self.city, self.country)
}
/// Constructs the path to some city-scoped data/input.
pub fn input_path<I: AsRef<str>>(&self, file: I) -> String {
path(format!(View on GitHub (pinned to 0964f29315)