type: integer, format: int64 generates a plain TypeScript number in most SDK generators, and JavaScript's number type can only represent integers exactly up to 2^53 - 1 (9,007,199,254,740,991) — well short of the full signed-64-bit range OpenAPI's int64 format describes. Any id, timestamp, or counter that exceeds that boundary gets silently rounded on the wire. Reproduced with a real generator 2026-09-21; the fix is to represent the field as a string, not a number, in the spec.
What does the generated type actually look like?
Take this schema:
openapi: 3.0.3
info:
title: Demo API
version: 1.0.0
paths:
/accounts/{accountId}:
get:
operationId: getAccount
parameters:
- name: accountId
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
id:
type: integer
format: int64
balanceCents:
type: integer
format: int64
Running this through OpenAPI Generator v7.25.0 (-g typescript-fetch) produces this real, unaltered output:
export interface GetAccount200Response {
id?: number;
balanceCents?: number;
}
No BigInt, no branded string type, no runtime range check — format: int64 and format: int32 generate the identical TypeScript type. The generator accepted the field, logged nothing, and produced code that compiles cleanly and fails only when a real value crosses the safe-integer boundary.
Why does this happen?
Because number is the only native numeric type JavaScript and TypeScript have, and it's a IEEE 754 double-precision float under the hood — it can exactly represent every integer up to Number.MAX_SAFE_INTEGER (2^53 - 1, or 9,007,199,254,740,991) and silently loses precision above that. int64, per the OpenAPI format registry, "represents a signed 64-bit integer, with the range -9223372036854775808 through 9223372036854775807" — a range roughly 1,000× larger than what a JavaScript number can hold exactly. The registry entry itself flags this and recommends the fix: "Representation as a JSON string is recommended for values outside the 53-bit range... as this avoids problems with recipients that parse JSON numbers into binary64 memory representation."
Most TypeScript-targeting code generators map format: int64 to number by default anyway, because that's the obvious mapping from "integer" and because most APIs' int64 fields — small counters, low timestamps — never actually exceed the safe range in practice. The bug is invisible until a specific id, usually one generated by a database sequence or a snowflake-style algorithm, finally crosses 2^53 — often long after the SDK shipped.
What does silent precision loss actually look like?
A large database-generated id like 9223372036854775807 (the maximum int64 value) parsed as a JavaScript number becomes 9223372036854775808 — the nearest representable double, off by one, with no error thrown anywhere in the parse. If that id is then used to make a follow-up request (GET /accounts/9223372036854775808), the client requests the wrong resource, and the failure surfaces as a 404 far from where the actual data loss happened.
How do you fix it?
Represent the field as a string in the schema, since JSON strings have no precision ceiling:
# Before — loses precision above 2^53-1 in JS/TS clients
properties:
id:
type: integer
format: int64
# After — full 64-bit range preserved as a string
properties:
id:
type: string
format: int64
example: "9223372036854775807"
type: string with format: int64 isn't a formal OpenAPI keyword combination defined by the base spec, but it's a widely-used pattern in large public APIs for exactly this reason — it signals "this is numeric data, transmitted as a string to avoid precision loss" to both human readers and generators that recognize the pattern, and it matches the OpenAPI format registry's own recommendation quoted above. Generated TypeScript for a type: string field is simply string, which callers then parse with BigInt(id) only when they need to do arithmetic on it — most consumers just pass the id back verbatim in a URL or a following request, where a string is exactly right.
Some generators go a step further with a dedicated format for this: as of September 2026, Speakeasy's TypeScript generator documents a distinct format: bigint (not int64) that maps a string-encoded big integer directly to a native BigInt value. If your generator has an equivalent, weigh it against the plain-string fix — mapping straight to BigInt is a bigger change to your SDK's public types than a string, since every caller now has to work with BigInt arithmetic instead of a plain string.
Does this affect every language, or just JavaScript/TypeScript?
Just JS/TS (and any other language whose only number type is a float, like older Lua). Statically-typed backend languages — Java, Go, C#, Python — all have a native 64-bit integer type and map format: int64 to it correctly with full precision. This is specifically a JavaScript-ecosystem problem, which makes it easy to miss if your API is tested primarily from a backend language and only breaks in the browser or a Node client.
How to catch this before it ships
- Audit every
format: int64field in your spec for whether its actual value range can exceedNumber.MAX_SAFE_INTEGER— most database auto-increment ids can, most small counters and enums can't. - Decide your convention once, spec-wide — string-typed large integers, or a documented
BigIntmapping in your generator config — rather than fixing fields one at a time as bugs get reported. - Run the spec through the free in-browser OpenAPI validator to see every
int64field in one pass before you generate a client from it.
If you're shipping a TypeScript SDK generated from an OpenAPI spec with large numeric ids, Sourced's hosted docs and SDK pipeline previews the actual generated TypeScript types — including exactly where int64 becomes a plain number — before you publish. Start a free report.
FAQ
What is JavaScript's Number.MAX_SAFE_INTEGER?
2^53 - 1, or 9,007,199,254,740,991. It's the largest integer JavaScript's number type (an IEEE 754 double) can represent without any risk of precision loss. Any integer above it may round to a different, nearby value.
Does format: int64 do anything at all if generators ignore the precision issue?
Yes — it tells human readers and some generators the field is a 64-bit integer conceptually, and generators for statically-typed languages (Java, Go, C#) do use it to pick a native 64-bit integer type. The gap is specifically in JavaScript/TypeScript output, where no native integer-only type exists below BigInt.
Should I use BigInt or a string for large int64 fields?
A string in the spec (type: string, format: int64) is the more portable fix — it works with every generator and every consumer language, and most consumers of an id field never need to do arithmetic on it. Native BigInt mapping is a generator-specific feature (not every tool supports it) and changes what type callers receive.
Will this show up as a validation error in a linter?
No. type: integer, format: int64 is fully valid OpenAPI and JSON Schema. This isn't a spec-validity problem — it's a downstream code-generation and runtime-precision problem that a schema linter has no way to flag, because the schema itself is correct.
Does this affect JSON.parse in general, not just generated SDKs?
Yes — any code that calls JSON.parse on a payload containing an integer above 2^53 - 1 loses precision at the parse step, regardless of whether the value came from a generated SDK, a hand-written fetch call, or a browser dev-tools inspection. The generated-SDK case matters most because it's the code path most teams trust without re-checking.
What about float/double formats — do they have the same problem?
No, for a different reason: format: float and format: double are already approximations by design, so JavaScript's double-precision number matches format: double almost exactly (both are IEEE 754 doubles). The int64 problem is specific to integers, where JSON and OpenAPI both promise exact values and JavaScript can't always deliver them.