You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Importing an OpenAPI 3.1.0 document produced by the default FastAPI + Pydantic v2
toolchain fails component validation when a schema pattern uses a lookahead. The
pattern is valid ECMA-262 (the dialect OAS 3.1 / JSON Schema 2020-12 defines pattern
against), but Tyk's vendored kin-openapi compiles patterns with Go's regexp (RE2),
which has no lookarounds. FastAPI/Pydantic generates these patterns automatically for
ordinary Decimal fields — users do not write them — so this affects a large share of
real-world Python services out of the box.
Both compile in V8 (new RegExp(...)) but not in Go's regexp (RE2 has no lookaround): regexp.Compile reports error parsing regexp: invalid or unsupported Perl syntax: `(?!` .
Root cause
OpenAPI 3.1.0 adopts JSON Schema 2020-12, whose pattern keyword is defined against the
ECMA-262 regex dialect; lookaheads are valid ECMA-262. Tyk validates via the vendored kin-openapi fork, which compiles every pattern with Go's standard-library regexp
(RE2). RE2 is a strict subset of ECMA-262 — it deliberately omits lookarounds,
lookbehinds and backreferences (to guarantee linear-time matching) and caps bounded
repetition at 1000. So a fully ECMA-262-valid pattern cannot compile under RE2, and the
whole document is rejected at import-time document/component validation (doc.Validate()),
distinct from request-time validation.
Why this matters
The constraint is framework-generated, so the JSON Schema guidance that authors keep to a
portable regex subset has no one to act on — there is no author in the loop, and users
cannot reasonably be expected to hand-edit Pydantic's generated schemas. A single
uncompilable pattern currently fails the entire import.
The problem is fully avoidable on the spec-producing side: rewrite the auto-generated Decimalpattern to an equivalent that stays inside the RE2 subset, leaving runtime
validation untouched (the emitted pattern is only a JSON-Schema annotation; Pydantic's
input validation does not use it). Done via Pydantic's public __get_pydantic_json_schema__
hook with no change to regex_engine.
The plain-Decimal case becomes:
^[+-]?(?:\d+\.?\d*|\.\d+)$
Constrained cases (max_digits / decimal_places) encode the digit budget by enumerating
the integer/fraction split instead of using a lookahead, e.g. max_digits=10, decimal_places=2 becomes:
Every such pattern was verified to compile under both Go's RE2 and V8 (ECMA-262), and to
accept/reject the same decimal strings as Pydantic's original. Sharing this only as
evidence that conformant-and-RE2-safe output exists — the fix that unblocks the broad
population of FastAPI/Pydantic users belongs in Tyk's import path (graceful degradation),
not in every user's schema.
Summary
Importing an OpenAPI 3.1.0 document produced by the default FastAPI + Pydantic v2
toolchain fails component validation when a schema
patternuses a lookahead. Thepattern is valid ECMA-262 (the dialect OAS 3.1 / JSON Schema 2020-12 defines
patternagainst), but Tyk's vendored
kin-openapicompiles patterns with Go'sregexp(RE2),which has no lookarounds. FastAPI/Pydantic generates these patterns automatically for
ordinary
Decimalfields — users do not write them — so this affects a large share ofreal-world Python services out of the box.
Environment
emitting this
pattern. Pydantic <= 2.11.x does not emit it.Error
Observed when importing a FastAPI-generated OpenAPI 3.1.0 document into Tyk Gateway 5.13.0.
Minimal repro
A nullable
Decimalfield, exactly as Pydantic 2.12+ emits it:{ "value": { "anyOf": [ { "type": "string", "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" }, { "type": "null" } ] } }A
Decimal(max_digits=10, decimal_places=2)field emits a second lookahead form:Both compile in V8 (
new RegExp(...)) but not in Go'sregexp(RE2 has no lookaround):regexp.Compilereportserror parsing regexp: invalid or unsupported Perl syntax:`(?!`.Root cause
OpenAPI 3.1.0 adopts JSON Schema 2020-12, whose
patternkeyword is defined against theECMA-262 regex dialect; lookaheads are valid ECMA-262. Tyk validates via the vendored
kin-openapifork, which compiles everypatternwith Go's standard-libraryregexp(RE2). RE2 is a strict subset of ECMA-262 — it deliberately omits lookarounds,
lookbehinds and backreferences (to guarantee linear-time matching) and caps bounded
repetition at 1000. So a fully ECMA-262-valid
patterncannot compile under RE2, and thewhole document is rejected at import-time document/component validation (
doc.Validate()),distinct from request-time validation.
Why this matters
The constraint is framework-generated, so the JSON Schema guidance that authors keep to a
portable regex subset has no one to act on — there is no author in the loop, and users
cannot reasonably be expected to hand-edit Pydantic's generated schemas. A single
uncompilable
patterncurrently fails the entire import.Requested behaviour
upstream (Invalid regex pattern fails silently on openapi3filter ValidateResponse/ValidateRequest then panics getkin/kin-openapi#1044).
pattern— skip that singleconstraint with a warning (graceful degradation, consistent with Tighten spec for regex patterns, to improve interoperability json-schema-org/json-schema-spec#816),
so default FastAPI/Pydantic specs import cleanly.
regexp2) for users who needlookaround.
Related
nil-pointer panic in
openapi3filter.ValidateRequestat request-validation time.repeat-count cap on
{0,4096}), showing this is a recurring RE2-subset class.acknowledges RE2-class engines cannot compile it.
scientific notation Pydantic itself serializes).
A conformant, RE2-safe reference
The problem is fully avoidable on the spec-producing side: rewrite the auto-generated
Decimalpatternto an equivalent that stays inside the RE2 subset, leaving runtimevalidation untouched (the emitted
patternis only a JSON-Schema annotation; Pydantic'sinput validation does not use it). Done via Pydantic's public
__get_pydantic_json_schema__hook with no change to
regex_engine.The plain-Decimal case becomes:
Constrained cases (
max_digits/decimal_places) encode the digit budget by enumeratingthe integer/fraction split instead of using a lookahead, e.g.
max_digits=10, decimal_places=2becomes:Every such pattern was verified to compile under both Go's RE2 and V8 (ECMA-262), and to
accept/reject the same decimal strings as Pydantic's original. Sharing this only as
evidence that conformant-and-RE2-safe output exists — the fix that unblocks the broad
population of FastAPI/Pydantic users belongs in Tyk's import path (graceful degradation),
not in every user's schema.