# The ISA segment is 106 characters of load-bearing whitespace

> transactions.dev · by visibility.cloud — 2026-07-31
> Written for the supply-chain ISV / traceability-vendor engineering org — the integration engineer whose partner file "won't parse".

One thesis: **the ISA segment is positional — 106 characters, fixed-width,
where the padding is part of the data — and once you parse it correctly,
most of what gets called "EDI is hard" turns out to have been envelope
trouble all along.** The document grammars have their complexities, but the
files that fail in production overwhelmingly fail before the first `ST`.

## Why fixed-width, in a delimited format

The history takes three sentences. X12 descends from formats designed for
machines that read records by byte offset, and the interchange header kept
that discipline so a receiver could read the envelope *before knowing the
delimiters* — because the envelope is where the delimiters are declared.
Everything after the ISA is delimited; the ISA itself cannot be, not
fully, because it bootstraps the delimiters. So its sixteen elements have
fixed widths, short values are padded with spaces to width, and the total
comes to exactly 106 characters including the terminator.

That is the whole trick, and it is also the whole trap: whitespace that
looks cosmetic is load-bearing, and an ISA of 104 or 107 characters is not
"close" — it is a different, wrong file.

## The byte ruler

A synthetic, license-clean interchange header, with positions annotated
(1-indexed; this description of positional layout is fact, in our own
words — element-by-element dictionary detail belongs to
[the licensed X12 reference at Stedi](https://www.stedi.com/edi/x12), which
we link and never republish):

```
ISA*00*          *00*          *ZZ*SELLERID       *ZZ*BUYERID        *260731*0915*U*00401*000000101*0*P*>~

bytes 1–3     "ISA"
byte  4       the element separator — declared by use; here "*"
bytes 5–6     authorization qualifier   (2, "00" = none)
bytes 8–17    authorization information (10, all spaces here — significant)
bytes 19–20   security qualifier        (2)
bytes 22–31   security information      (10, spaces again)
bytes 33–34   sender ID qualifier       (2, "ZZ" = mutually defined)
bytes 36–50   sender ID                 (15, "SELLERID" + 7 pad spaces)
bytes 52–53   receiver ID qualifier     (2)
bytes 55–69   receiver ID               (15, "BUYERID" + 8 pad spaces)
bytes 71–76   date                      (6, YYMMDD)
bytes 78–81   time                      (4, HHMM)
byte  83      repetition separator in 005010+; standards ID "U" in 004010
bytes 85–89   version                   (5, "00401")
bytes 91–99   interchange control number (9, zero-padded)
byte  101     acknowledgment requested  (1)
byte  103     usage                     (1, "P" production / "T" test)
byte  105     the component (sub-element) separator — here ">"
byte  106     the segment terminator — here "~"; everything after ISA obeys it
```

Three of those positions are not data at all but *declarations*: byte 4
teaches the receiver the element separator, byte 105 the component
separator, byte 106 the segment terminator. The envelope describes its own
syntax — the full consequences of that are
[the delimiter post](/blog/byte-4-is-the-law/).

## The five envelope failures we see most

The fuzz corpus generates envelope pathologies by the hundred thousand;
production files keep reproducing the same five. Each produces a typed
error from `parse` — a machine-readable code with a position, on stderr,
never a shrug:

- **Short or long ISA.** A sender's system trimmed trailing spaces from
  ISA06 or ISA08 — the file is now 104 characters to the first terminator
  and every subsequent offset is wrong. Typed as an envelope-length error
  with the observed length; the fix is the sender's padding, not your
  parser's tolerance.
- **Wrong padding character.** Zero-padded alphanumeric IDs or, worse,
  tab-padded ones — visually identical in most editors, byte-distinct on
  the wire. The error cites the offending byte and offset.
- **CRLF drift.** The terminator is `~` but the file was FTP'd in text
  mode and every segment now ends `~\r\n` — or the terminator *is* the
  newline and a transfer normalized CRLF to LF, silently editing the data.
  Typed as a terminator-consistency error; the parse does not guess which
  convention was meant.
- **Eight-bit characters.** A partner's name field carries a byte outside
  the basic character set — fine in some receivers, fatal in others.
  Flagged with position and byte value, because "works with our other
  partners" is not a parse verdict.
- **Terminator collision.** The declared component separator or terminator
  also appears inside data — legal-looking, structurally ruinous, and the
  reason a correct parser trusts the declarations and only the
  declarations rather than sniffing for "likely" delimiters.

Whether the *counts and control numbers* inside a structurally sound
envelope agree with themselves is the next layer of failure, and it has
its own post: [the control-number handshake](/blog/the-envelope-must-agree-with-itself/).

## The honest claim

Parse the envelope right — fixed-width ISA, declared delimiters, typed
rejections with byte positions — and roughly half of the folk reputation
of EDI evaporates. Not the interesting half: the document grammars, the
partner-guide variation, and the semantics of compiling documents onto
event records (which is this site's actual subject — start at
[the 856 compile](/blog/the-856-is-already-an-aggregation-tree/), and see
what a conformant record buys at
[epcis.dev](https://epcis.dev/business-transactions-in-epcis/)) all remain.
But the 2 a.m. page about a partner file that "won't parse" is, in our
corpus, an envelope story far more often than a grammar story — and
envelope stories end fast when the error names its byte.

`parse` is the first verb in the pipeline and the one every other verb
stands on: bytes in, lossless canonical JSON out, typed error with a
position when the envelope lies. If your partner files have a folder of
known horrors, that folder is exactly what
[the access flow](/get-access/) wants to hear about — the fuzz corpus
grew out of files like yours.

---
Access: https://transactions.dev/get-access/ · All posts: https://transactions.dev/blog/ · Machine face: https://transactions.dev/llms.txt
