Hi team — thanks for maintaining this SDK. Filing a quick one we hit when running map-matching against German autobahn traces.
Summary
TraceEdge.speed_limit is annotated Optional[StrictInt], but the field's own docstring acknowledges that the value can be the string "unlimited". Any trace_attributes response that includes an unrestricted-speed edge — common for German autobahn segments — triggers a pydantic.ValidationError deep inside the SDK's response deserialization. Because the failure happens before the response object is returned to the caller, there's no API surface for swallowing or coercing it; the whole call dies.
Environment
| thingy |
version |
stadiamaps |
8.1.0 (latest on PyPI) |
| Python |
3.13.11 |
pydantic |
v2 (whatever the SDK pins) |
Minimal reproduction
No API key, no network call — the bug is purely in the model definition. Save as repro.py and run:
from stadiamaps.models.trace_edge import TraceEdge
TraceEdge.from_dict({
"road_class": "motorway",
"surface": "paved_smooth",
"speed_limit": "unlimited",
})
$ pip install 'stadiamaps==8.1.0' && python repro.py
Actual
pydantic_core._pydantic_core.ValidationError: 1 validation error for TraceEdge
speed_limit
Input should be a valid integer [type=int_type, input_value='unlimited', input_type=str]
For further information visit https://errors.pydantic.dev/2.13/v/int_type
Expected
TraceEdge.from_dict({..., "speed_limit": "unlimited"}) succeeds — either preserving the string for callers who want to surface it, or coercing it to None if the SDK doesn't want to model a polymorphic value. The current behaviour means that any unrestricted-speed-limit edge in a trace response crashes deserialization for every edge in the batch.
Where the bug lives
stadiamaps/models/trace_edge.py:73:
speed_limit: Optional[StrictInt] = Field(
default=None,
description=(
"The speed limit along the edge measured in `units`/hr. "
"This may be either an integer or the string \"unlimited\" "
"if speed limit data is available. ..."
),
)
The free-form description tells the truth; the type annotation doesn't.
Real-world impact
Any trace covering a German autobahn segment with no posted speed limit. We hit this on a Munich → Nuremberg test route on the A9 — Eching to Schweitenkirchen. Roughly 1 in 2 trace_attributes calls in our German pilot fleet crash because of this.
Suggested fix
The cleanest fix lives in the OpenAPI spec (and then flows through codegen), not in the generated Python. A polymorphic schema would let the codegen emit a Union type:
speed_limit:
description: ...
oneOf:
- type: integer
- type: string
enum: [unlimited]
Codegen would then emit Union[int, Literal["unlimited"]] | None (or whatever the generator's idiom is) and the field validates cleanly in both cases.
If a spec change is too heavy a lift, a field_validator in the SDK that coerces "unlimited" to None (or some sentinel) would also fix it, at the cost of losing the unlimited marker.
Workaround we're using
For anyone landing here from a search engine while waiting for a fix — we monkey-patch TraceEdge.from_dict at import time to coerce "unlimited" → None before model_validate runs. Tiny, ugly, removable as soon as a fixed version is pinned.
from stadiamaps.models.trace_edge import TraceEdge
_original_from_dict = TraceEdge.from_dict
@classmethod
def _patched_from_dict(cls, obj):
if isinstance(obj, dict) and obj.get("speed_limit") == "unlimited":
obj = {**obj, "speed_limit": None}
return _original_from_dict.__func__(cls, obj)
TraceEdge.from_dict = _patched_from_dict
Happy to send a PR against the spec if you can point at the upstream source repo (the SDK source on this GitHub is auto-generated, so I'd guess the actual spec lives elsewhere).
Thanks!
Hi team — thanks for maintaining this SDK. Filing a quick one we hit when running map-matching against German autobahn traces.
Summary
TraceEdge.speed_limitis annotatedOptional[StrictInt], but the field's own docstring acknowledges that the value can be the string"unlimited". Anytrace_attributesresponse that includes an unrestricted-speed edge — common for German autobahn segments — triggers apydantic.ValidationErrordeep inside the SDK's response deserialization. Because the failure happens before the response object is returned to the caller, there's no API surface for swallowing or coercing it; the whole call dies.Environment
stadiamaps8.1.0(latest on PyPI)3.13.11pydanticMinimal reproduction
No API key, no network call — the bug is purely in the model definition. Save as
repro.pyand run:Actual
Expected
TraceEdge.from_dict({..., "speed_limit": "unlimited"})succeeds — either preserving the string for callers who want to surface it, or coercing it toNoneif the SDK doesn't want to model a polymorphic value. The current behaviour means that any unrestricted-speed-limit edge in a trace response crashes deserialization for every edge in the batch.Where the bug lives
stadiamaps/models/trace_edge.py:73:The free-form description tells the truth; the type annotation doesn't.
Real-world impact
Any trace covering a German autobahn segment with no posted speed limit. We hit this on a Munich → Nuremberg test route on the A9 — Eching to Schweitenkirchen. Roughly 1 in 2 trace_attributes calls in our German pilot fleet crash because of this.
Suggested fix
The cleanest fix lives in the OpenAPI spec (and then flows through codegen), not in the generated Python. A polymorphic schema would let the codegen emit a
Uniontype:Codegen would then emit
Union[int, Literal["unlimited"]] | None(or whatever the generator's idiom is) and the field validates cleanly in both cases.If a spec change is too heavy a lift, a
field_validatorin the SDK that coerces"unlimited"toNone(or some sentinel) would also fix it, at the cost of losing the unlimited marker.Workaround we're using
For anyone landing here from a search engine while waiting for a fix — we monkey-patch
TraceEdge.from_dictat import time to coerce"unlimited"→Nonebeforemodel_validateruns. Tiny, ugly, removable as soon as a fixed version is pinned.Happy to send a PR against the spec if you can point at the upstream source repo (the SDK source on this GitHub is auto-generated, so I'd guess the actual spec lives elsewhere).
Thanks!