a-b-street/abstreet · error · anyhow::Error

Unknown command

Error message

Unknown command

What it means

The catch-all arm of handle_command's match on the request path returns Err(anyhow!("Unknown command")) for any path that is not one of the recognized headless API routes. It signals the client called an endpoint this server does not implement.

Solutions

  1. Check the exact endpoint names in handle_command's match arms and correct the request path
  2. Verify client and server are from the same version of the codebase
  3. Note the error response body's context for the path and adjust the caller

Example fix

// before
GET /sim/fast-forward
// after
GET /sim/ffwd
Defensive patterns

Strategy: validation

Validate before calling

// check the path against known endpoints before calling
const KNOWN = ['/sim/reset', '/sim/ffwd', '/geo/roads', ...];
if (!KNOWN.includes(path)) console.warn('unknown endpoint', path);

Try / catch

let resp = client.get(path).await?;
if resp.text().await?.contains("Unknown command") {
    // wrong endpoint; refresh client API against server version
}

Prevention

When it happens

Trigger: HTTP request to a path not in handle_command's match list (e.g. /sim/ffwd typo'd as /sim/fastforward, wrong trailing slash, or an endpoint added on the client but not the server).

Common situations: API contract drift between client and headless server version; typos in endpoint paths; hitting the server with a probe/health-check path it doesn't know.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at headless/src/main.rs:418

        }
        "/map/get-intersection-geometry" => {
            let i = IntersectionID(get("id")?.parse::<usize>()?);
            Ok(abstutil::to_json(&export_geometry(map, i)))
        }
        "/map/get-all-geometry" => Ok(abstutil::to_json(&map.export_geometry())),
        "/map/get-nearest-road" => {
            let pt = LonLat::new(get("lon")?.parse::<f64>()?, get("lat")?.parse::<f64>()?);
            let mut closest = FindClosest::new();
            for r in map.all_roads() {
                closest.add(r.id, r.center_pts.points());
            }
            let threshold = Distance::meters(get("threshold_meters")?.parse::<f64>()?);
            match closest.closest_pt(pt.to_pt(map.get_gps_bounds()), threshold) {
                Some((r, _)) => Ok(r.0.to_string()),
                None => bail!("No road within {} of {}", threshold, pt),
            }
        }
        _ => Err(anyhow!("Unknown command")),
    }
}

// TODO I think specifying the API with protobufs or similar will be a better idea.

#[derive(Serialize)]
struct FinishedTrip {
    id: TripID,
    person: PersonID,
    duration: Option<Duration>,
    distance_crossed: Distance,
    mode: TripMode,
}

#[derive(Serialize)]
struct Delays {
    #[serde(serialize_with = "serialize_btreemap")]
    per_direction: BTreeMap<MovementID, Vec<Duration>>,

View on GitHub (pinned to 0964f29315)