A pallet rolls into the dock. Across the front face is a single 1D barcode about the width of a forearm. The receiving system scans it and you can see the payload arrive in the warehouse software: 0107640156790009ABC123 26013 1 4225. Half of those characters belong to the GTIN, half to the batch number, the rest are an expiry and a serial — but the parser has glued them together because the FNC1 separators between Application Identifiers were dropped on the way through the scanner driver. The barcode is perfectly correct. The configuration around it isn't.
That's a typical encounter with Code 128 — the workhorse 1D symbology behind almost every shipping label, pallet tag and asset sticker in logistics. It encodes the full 128-character ASCII range in a compact form, switches between three subsets to pack numeric data tighter, and gains a structured-data flavour as GS1-128 when it carries an FNC1 at the head. This guide explains Code 128 explained at a level you can act on: the three subsets, the FNC1 modifier, a worked GS1-128 with every byte labelled, the scanner configuration that bites people first, and how to tell Code 128 vs Code 39 when you only have the printed value in front of you.
The short answer
Code 128 is a 1D barcode that encodes all 128 ASCII characters, used almost exclusively in logistics and asset tracking rather than retail POS. It picks between three internal subsets (A, B and C) to keep the symbol as narrow as possible. When the first character is an FNC1, the same barcode is called GS1-128 and the payload is read as GS1 Application Identifiers — GTIN, batch, expiry, serial, pallet number.
SHIP-2026-00042SHIP-2026-00042.Plain Code 128 — any ASCII payload, no GS1 parsing.
(00)103456789012345677(00) followed by the 18-digit serial.Code 128 with FNC1 — payload follows GS1 AI syntax.
Code 128 at a glance
Where Code 128 fits depends on what else you considered. Most teams pick between Code 128, Code 39 and GS1-128 — they look similar at a glance and the wrong choice surfaces months later when the data won't parse cleanly.
| Aspect | Code 128 | Code 39 | GS1-128 |
|---|---|---|---|
| Character set | All 128 ASCII characters | Uppercase + digits + 7 symbols (43 total) | Subset of Code 128 with GS1 AIs only |
| Lowercase | Yes (via Subset B) | No | Yes (inside AI 10, 21, etc.) |
| Density | High; double-density for pure numeric (Subset C) | Low — about 2× wider than Code 128 for the same payload | High; same as Code 128 |
| Structured data | No (free-form ASCII) | No (free-form, upper only) | Yes — GS1 Application Identifiers separated by FNC1 |
| FNC1 | Optional | Not supported | Mandatory as first character |
| Where you see it | Shipping labels, asset stickers, hospital wristbands | Legacy automotive, defence, library catalogues | GS1 logistics labels, SSCC pallet IDs, traceability data |
The three subsets: A, B and C
The clever part of Code 128 is that it doesn't encode characters one at a time. Instead it carries an internal state that selects one of three subsets and switches between them mid-symbol with a single shift character. The encoder picks whichever subset makes the next few characters cheapest, and the decoded text comes out as a clean ASCII string with no trace of the underlying gymnastics.
- Subset A — ASCII 0 through 95: uppercase letters, digits and control codes (NUL, GS, RS, FS and the rest). Used mainly in legacy systems that need to embed control characters in the payload.
- Subset B — ASCII 32 through 127: uppercase, lowercase, digits and punctuation. The default for general-purpose alphanumeric text — "Code 128 supports lowercase" means Subset B.
- Subset C — pairs of digits from
00to99encoded as one symbol character. The killer feature for pure numeric payloads: a 12-digit value takes six Subset C characters instead of twelve Subset B characters, halving the printed symbol width.
You never have to think about subsets when decoding — the scanner returns plain ASCII either way. The reason to know about them is when you're building a Code 128 from scratch and the symbol comes out wider than expected: usually a numeric run that the encoder failed to fold into Subset C.
FNC1 and GS1-128
FNC1 is the function character that turns a plain Code 128 into a GS1-128. It plays two roles:
- Mode flag. An FNC1 immediately after the Code 128 start character marks the entire symbol as a GS1 carrier. Without that flag the payload is read as free-form ASCII; with it, parsers expect GS1 Application Identifiers.
- Variable-length separator. Some AIs are fixed length (GTIN is always 14 digits inside AI 01; the expiry date inside AI 17 is always 6 digits). Others — batch (10), serial (21), customer reference (240) — are variable up to their maximum. The encoder appends an FNC1 immediately after a variable-length AI so the parser knows the field has ended. Fixed-length AIs don't need a trailing FNC1 because their length is known from the AI itself.
A worked GS1-128 example
Take the worked example used throughout the GS1 logistics guides: (01)07640156790009(10)ABC123(17)261231. It carries a GTIN, a batch number and an expiry date in a single GS1-128 symbol.
(01)07640156790009(10)ABC123(17)26123107640156790009, batch ABC123, expiry 2026-12-31 — three AIs, one symbol.Reading the symbol left-to-right, the parser walks through it like this:
- Start C + FNC1. The encoder chose Subset C because the payload opens with a long numeric run. The FNC1 immediately after the start character marks the symbol as GS1-128.
- AI (01) — GTIN-14. Fixed length: 14 digits.
07640156790009is the product identifier. No trailing FNC1 needed because GS1 knows AI 01 is exactly 14 digits. - AI (10) — Batch. Variable length, up to 20 alphanumeric characters. The encoder switched out of Subset C into Subset B for
ABC123, then appended an FNC1 to mark the end of the variable field. - AI (17) — Expiry. Fixed length: 6 digits (YYMMDD).
261231means 31 December 2026. - Check digit + stop. Code 128 finishes with a modulo-103 checksum and a stop pattern, both invisible in the decoded ASCII.
Paste the same value into the decoder and you can see each AI laid out with its spec name and validation status:
Decode any GS1-128 element string into structured GTIN + batch + expiry data.
Where you actually see it
Code 128 is the dominant 1D symbology of the back-of-house. You meet it everywhere a workflow needs to encode more than a GTIN but doesn't justify a 2D code:
- Shipping labels. Tracking numbers from FedEx, UPS, DHL and Royal Mail are almost always Code 128 — alphanumeric, predictable length, easy to print on a thermal label.
- Pallet and case tags. The GS1 logistics label uses GS1-128 to carry the SSCC (AI 00) and, where required, the GTIN of the case contents (AI 02) plus batch and expiry.
- Asset and inventory stickers. Internal labels for equipment, shelves and reusable containers — "ASSET-2026-00042" style — sit on plain Code 128 with no GS1 layer.
- Healthcare wristbands. Patient ID bands often use Code 128 for its lowercase support and its ability to fit twenty-plus characters on a 10 mm-tall strip.
- Library and archive catalogues. Some legacy systems still emit Code 39 here; modern ones moved to Code 128 for the density.
To roll out new labels yourself, the Barcode Generator covers plain Code 128 with the encoding controls exposed; the ITF-14 Carton Label Generator is the right tool when the carton-level GTIN is the main subject.
Decoding Code 128 in software
The most common Code 128 bug isn't in the barcode — it's in the configuration between the scanner and the application that receives the data. Three settings cover most of the failures:
- FNC1 transmission. Configure the scanner to emit FNC1 as ASCII 29 (
GS) — the GS1 default. Some scanners are set to emit FNC1 as a blank character, which is what destroys the example at the top of this guide. - Prefix / suffix. Many scanners append a carriage return or tab on every read. That's fine for keyboard-wedge workflows; less fine when you're piping the raw payload into a regex.
- Code-set hint. A handful of scanners can be asked to prefix every Code 128 read with
]C1to signal a GS1-128 even when the payload doesn't open with FNC1. Useful for catching mis-formatted symbols upstream.
If you have a payload but aren't sure whether it's Code 128, Code 39 or something else entirely, the Barcode Type Detector will tell you in one step:
Heuristically identify the most likely 1D or 2D symbology behind a raw value.
For round-trip QA — scanning your own printed Code 128 to verify it decodes cleanly — use the Online Barcode Reader from a phone or webcam. For bulk validation across hundreds of historical payloads, paste the list into the Batch Barcode Tools.
Common mistakes
The repeat offenders that turn a perfectly good Code 128 into a downstream parsing headache:
- Splitting GS1-128 AIs on a hard-coded character. Variable-length fields end up concatenated with the next AI when FNC1 is stripped. Always split on the configured FNC1 replacement (typically ASCII 29).
- Sending mixed-case text through a Subset A encoder. Lowercase letters silently disappear because Subset A doesn't cover them. Use Subset B (or let the encoder pick).
- Printing too narrow. Code 128 needs a minimum X-dimension (single-module width) to print and scan reliably. Shrink below it for label real-estate reasons and the symbol grade collapses on the first scratch.
- Confusing Code 128 with Code 39. Code 39 can't encode lowercase and is roughly 2× wider for the same payload. If your "Code 128" looks twice as long as the same data printed elsewhere, it's probably Code 39.
- Assuming GS1-128 implies a specific AI order. AIs can appear in any order — what matters is the FNC1 after every variable-length one. Reorder them all you like during artwork QA; the parser doesn't care.
Quick decision guide
When you only need the practical answer for a new label, the choices collapse to four short ones:
- Choose Code 128 when the payload is alphanumeric, you don't need GS1 structured data, and the label has room for a 1D symbol — internal asset tags, shipping labels, asset stickers.
- Choose GS1-128 when the payload carries GS1 Application Identifiers — SSCC pallet IDs, GTIN+batch+expiry on cartons, traceability data inside the logistics label.
- Avoid Code 39 unless you're forced to by a legacy reader. It's wider than Code 128 for the same data and can't carry lowercase.
- Move to 2D (Data Matrix or QR) when the payload pushes past ~40 characters, when the label is square rather than wide, or when you want the Sunrise-2027-ready GS1 Digital Link form on the consumer pack.
Frequently asked
Why are there three Code 128 subsets?
Subsets A, B and C let Code 128 encode the full ASCII range efficiently. Subset A covers ASCII 0–95 (uppercase + control codes), B covers 32–127 (uppercase + lowercase + punctuation), and C encodes pairs of digits in a single symbol character — halving the symbol width for numeric data. Encoders switch between subsets mid-symbol using shift characters.
What is GS1-128 and how is it different?
GS1-128 (formerly UCC/EAN-128) is Code 128 with a mandatory FNC1 as the first symbol character, signalling that the payload follows GS1 Application Identifier syntax. Same barcode symbology underneath, with stricter parsing rules layered on top.
What is FNC1 actually for?
FNC1 plays two roles inside a GS1-128. The first FNC1 immediately after the start character marks the symbol as a GS1 carrier rather than a plain Code 128. Subsequent FNC1 characters act as separators between variable-length Application Identifiers — so the parser knows where AI (10) batch ends and the next AI begins.
Why does my Code 128 scanner output extra characters?
Usually it's the FNC1 separator being transmitted as a control character — most commonly ASCII 29 (GS). Configure your scanner or middleware to translate FNC1 into a delimiter your software can split on. The same trick lets you parse variable-length AIs without ambiguity.
Is Code 128 used at retail POS?
Rarely on consumer packaging — retail POS expects EAN or UPC. Code 128 dominates the back end: shipping labels, pallet tags, internal asset stickers and the GS1 logistics label. It's a workhorse of the warehouse, not the shelf.
How long can a Code 128 barcode be?
There's no hard maximum, but practical limits kick in around 30–80 characters because the symbol gets too wide to scan reliably with a typical laser. For longer payloads, switch to a 2D format like Data Matrix or QR.
References
- ISO/IEC 15417:2007 — the international standard that defines the Code 128 symbology (subsets, FNC1, checksum algorithm). Available from the ISO catalogue.
- GS1 General Specifications — defines GS1-128, the Application Identifier table and the parsing rules. Published by GS1 and updated annually.
- GS1 Logistics Label guide — the practical reference for SSCC, AI ordering and label layout on cases and pallets: gs1.org/standards/barcodes/gs1-128.
Need to generate Code 128 or GS1-128 labels yourself? The Barcode Generator covers plain Code 128, and the GS1 Digital Link Generator builds a Digital Link URL from the same GTIN + AIs in case you're also planning a 2D carrier on the consumer pack.