a-b-street/abstreet · error
isn't a traffic signal
Error message
{} isn't a traffic signal What it means
The /traffic-signals/get command looks up an intersection and returns its ControlTrafficSignal state. Only intersections actually controlled by a traffic signal exist in the signals map; if the given IntersectionID is an stop sign, border, or uncontrolled intersection, the library bails with this error.
Solutions
- Verify the intersection is a traffic signal (i.is_traffic_signal()) before querying; enumerate map.all_lts / signal IDs instead of guessing.
- Get the ID from the map itself (e.g. by filtering intersections whose control is a signal) rather than hardcoding.
- Refresh your ID references after re-importing the map, since control types can change between imports.
Defensive patterns
Strategy: validation
Validate before calling
// enumerate only signalized intersections from the map first const signalIds = map.intersections.filter(i => i.is_traffic_signal()).map(i => i.id);
Type guard
function isTrafficSignal(i) { return i.control === "TrafficSignal"; } Try / catch
try {
ts = await post("/traffic-signals/get", { id });
} catch (e) {
if (String(e).includes("isn't a traffic signal")) ts = null;
} Prevention
- Only query intersections known to be traffic signals
- Refresh intersection IDs after map re-imports
- Don't assume control types persist across imports
When it happens
Trigger: POST /traffic-signals/get with id=<usize> where maybe_get_traffic_signal(IntersectionID(id)) returns None — the intersection exists but isn't signalized.
Common situations: Iterating all intersection IDs instead of only traffic-signal IDs; map re-imports that changed an intersection's control type; hardcoded IDs from a different map.
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
- is in the past. call /sim/reset first?
- It's now, so you can't start a trip at
- No road within of
- Traffic signal assignment for
- Traffic signal has conflicting protected movements in one…
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/82f2d2725f466aee.
Report an issue: GitHub.
Appendix: source
Thrown at headless/src/main.rs:220
}
}
let mut scenario = Scenario::empty(map, "one-shot");
scenario.people = ExternalPerson::import(map, vec![input], false)?;
let mut rng = XorShiftRng::seed_from_u64(load.rng_seed);
sim.instantiate(&scenario, map, &mut rng, &mut Timer::throwaway());
Ok(format!(
"{} created",
sim.get_all_people().last().unwrap().id
))
}
// Traffic signals
"/traffic-signals/get" => {
let i = IntersectionID(get("id")?.parse::<usize>()?);
if let Some(ts) = map.maybe_get_traffic_signal(i) {
Ok(abstutil::to_json(ts))
} else {
bail!("{} isn't a traffic signal", i)
}
}
"/traffic-signals/set" => {
let ts: ControlTrafficSignal = abstutil::from_json(body)?;
let id = ts.id;
// incremental_edit_traffic_signal is the cheap option, but since we may need to call
// get-edits later, go through the proper flow.
let mut edits = map.get_edits().clone();
edits.commands.push(map.edit_intersection_cmd(id, |new| {
new.control = EditIntersectionControl::TrafficSignal(ts.export(map));
}));
map.must_apply_edits(edits, &mut Timer::throwaway());
map.recalculate_pathfinding_after_edits(&mut Timer::throwaway());
Ok(format!("{} has been updated", id))
}
"/traffic-signals/get-delays" => {View on GitHub (pinned to 0964f29315)