Skip to content

Bug: TraceEdge.speed_limit rejects "unlimited" despite the docstring documenting it as a valid value #3

Description

@Nambu14

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions