OpenAPI errors

How to fix int64 precision loss in TypeScript OpenAPI clients

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

  1. Audit every format: int64 field in your spec for whether its actual value range can exceed Number.MAX_SAFE_INTEGER — most database auto-increment ids can, most small counters and enums can't.
  2. Decide your convention once, spec-wide — string-typed large integers, or a documented BigInt mapping in your generator config — rather than fixing fields one at a time as bugs get reported.
  3. Run the spec through the free in-browser OpenAPI validator to see every int64 field 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.