a-b-street/abstreet · error

Unknown mode

Error message

Unknown mode {}

What it means

get_mode maps the Soundcast trip file's mode column codes to a TripMode (Drive for 3.0-5.0, Transit for 6.0-8.0, Walk for 0.0, etc.). Any unrecognized mode string panics with "Unknown mode {code}" during scenario import. Strictness is deliberate so unknown modal codes aren't silently miscategorized.

Solutions

  1. Look up the offending mode code in the psrc/soundcast wiki (_trip.tsv docs) and either fix the data or add a mapping arm.
  2. Default unknown codes to TripMode::Walk (as already done for the invalid "0.0" code) instead of panicking, if approximate mapping is acceptable.
  3. Pre-validate the trip file's mode column against the known set before running the import.
  4. Regenerate inputs with the matching Soundcast model version.

Example fix

// before
_ => panic!("Unknown mode {}", code),
// after
other => {
    warn!("Unknown mode {}, treating as walk", other);
    TripMode::Walk
}
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

// Panics uncatchably; coerce before import:
let mode = if is_known_mode(raw) { raw } else { "0.0".to_string() };

Prevention

When it happens

Trigger: Calling import_trips on a _trip.tsv whose mode column contains a value outside the mapped set (e.g. "9.0", "NA", blank, or codes from a different model version).

Common situations: Newer PSRC Soundcast releases adding mode categories; malformed TSV rows; using trip files from a different regional model with different coding; hand-edited input data.

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/a31103179602ace7. Report an issue: GitHub.

Appendix: source

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

        "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),
    }
}

// See https://github.com/psrc/soundcast/wiki/Outputs#trip-file-_triptsv
//
// A/B Street flattens a person's trips into a simple list, but the Soundcast model is more
// detailed:
//
// A person takes 1+ tours a day. Each tour starts and ends at the same place (usually home) and
// has some primary destination. A tour has two legs (to the destination, then returning from it),
// each split into individual trips.
//
// An example: someone takes the bus to work, but stops for a coffee and walks the final bit to
// work. Then later they bus home. This would be encoded like so:
//
// - Tour 1 (purpose work), leg = to destination, trip 1 (purpose eat, using transit)
// - Tour 1 (purpose work), leg = to destination, trip 2 (purpose work, walking)
// - Tour 1 (purpose work), leg = return from destination, trip 1 (purpose home, using transit)

View on GitHub (pinned to 0964f29315)