a-b-street/abstreet · error

Unknown dpurp

Error message

Unknown dpurp {}

What it means

get_purpose maps the Soundcast trip file's dpurp column (a string code like "1.0".."10.0") to a TripPurpose enum. Any dpurp value outside the enumerated set panics with "Unknown dpurp {code}". The import is intentionally strict: an unmodeled purpose would silently corrupt scenario data.

Solutions

  1. Inspect the offending row's dpurp value in the trip file and correct or impute a known code.
  2. Add a mapping arm for the new code (or a default like TripPurpose::Home / Err) in get_purpose if the model version legitimately introduced it.
  3. Pre-filter/normalize the TSV (map blank/NA to "0.0" = not home-to-home per the wiki) before import.
  4. Pin to the Soundcast model version the importer was written against (see psrc/soundcast wiki Outputs docs).

Example fix

// before
_ => panic!("Unknown dpurp {}", code),
// after
other => {
    warn!("Unknown dpurp {}, defaulting to Home", other);
    TripPurpose::Home
}
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN_DPURP: [&str; 11] = ["0.0","1.0","2.0","3.0","4.0","5.0","6.0","7.0","8.0","9.0","10.0"];
let bad: Vec<_> = rows.iter().filter(|r| !KNOWN_DPURP.contains(&r.dpurp.as_str())).collect();
assert!(bad.is_empty(), "unknown dpurp values: {:?}", bad);

Type guard

fn is_known_dpurp(code: &str) -> bool {
    matches!(code, "0.0"|"1.0"|"2.0"|"3.0"|"4.0"|"5.0"|"6.0"|"7.0"|"8.0"|"9.0"|"10.0")
}

Try / catch

// Panics uncatchably; sanitize before import:
let dpurp = if is_known_dpurp(raw) { raw } else { "0.0".to_string() };

Prevention

When it happens

Trigger: Calling import_trips on a _trip.tsv whose dpurp column contains a value not in 0.0-10.0 (e.g. empty string, "11.0", "NA", non-numeric junk).

Common situations: A newer Soundcast model version changing the purpose codes; corrupted or hand-edited trip files; regional model variants (different PSRC output years) using extra categories; blank cells in the TSV.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at importer/src/soundcast/popdat.rs:250

) -> (HashMap<usize, Endpoint>, BTreeMap<usize, ExtraShape>) {
    panic!("Can't import_parcels for popdat.bin without the scenarios feature (GDAL dependency)");
}

// From https://github.com/psrc/soundcast/wiki/Outputs#trip-file-_triptsv, dpurp
fn get_purpose(code: &str) -> TripPurpose {
    match code {
        "0.0" => TripPurpose::Home,
        "1.0" => TripPurpose::Work,
        "2.0" => TripPurpose::School,
        "3.0" => TripPurpose::Escort,
        "4.0" => TripPurpose::PersonalBusiness,
        "5.0" => TripPurpose::Shopping,
        "6.0" => TripPurpose::Meal,
        "7.0" => TripPurpose::Social,
        "8.0" => TripPurpose::Recreation,
        "9.0" => TripPurpose::Medical,
        "10.0" => TripPurpose::ParkAndRideTransfer,
        _ => panic!("Unknown dpurp {}", code),
    }
}

// From https://github.com/psrc/soundcast/wiki/Outputs#trip-file-_triptsv, mode
fn get_mode(code: &str) -> TripMode {
    match code {
        "1.0" => TripMode::Walk,
        "2.0" => TripMode::Bike,
        "3.0" | "4.0" | "5.0" => TripMode::Drive,
        // TODO Park-and-ride and school bus as walk-to-transit is a little weird.
        "6.0" | "7.0" | "8.0" => TripMode::Transit,
        // TODO Invalid code, what's this one mean? I only see a few examples, so just default to
        // walking.
        "0.0" => TripMode::Walk,
        _ => panic!("Unknown mode {}", code),
    }
}

View on GitHub (pinned to 0964f29315)