Zebra printer prints the ZPL code as text instead of the label
You send this:
^XA^FO50,50^A0N,45,45^FDHello^FS^XZ
and the printer feeds a label with ^XA^FO50,50^A0N,45,45^FDHello^FS^XZ printed
across it in small type. Sometimes it wraps across several labels.
Your ZPL is fine. The printer received it, understood it as characters, and did exactly what it was told. Something upstream told it to treat the stream as text rather than as a format to run.
There are three causes, and you can tell them apart in about a minute.
Cause 1: the printer is in diagnostic (dump) mode
This is the common one, and it is sticky: ~JD puts a Zebra into diagnostic
mode, where it prints every byte it receives instead of interpreting it, and it
stays there across power cycles until something sends ~JE. People arrive
here after following a troubleshooting post, or from a colleague who left it on
months ago.
Ask the printer
~HS returns three lines of status. Send it and read the seventh field of the
first line, which is the diagnostic-mode flag:
(printf '~HS'; sleep 2) | nc 192.168.1.157 9100
Real output from a GX430t:
030,0,0,1824,002,0,0,0,000,0,0,0
001,0,0,0,1,2,6,0,00000000,1,000
1234,0
Counting the first line: 030 0 0 1824 002 0 0 0 000 0 0 0.
That seventh field is 0, so this printer is not in diagnostic mode. A 1
there is your answer, and you can stop reading.
While you are looking, the fourth field (1824) is the label length in dots,
which is useful for a different problem: at 12 dpmm that is 152 mm, a correct
6-inch label.
Fix
printf '~JE' | nc 192.168.1.157 9100
That is the whole fix. Print again.
Cause 2: the printer is not listening for ZPL
Zebra printers can speak more than one language, and the desktop models ship
speaking EPL as well as ZPL. If the active language does not include ZPL, your
^XA is just characters.
Ask over SGD:
(printf '! U1 getvar "device.languages"\r\n'; sleep 1) | nc 192.168.1.157 9100
Real answer from the same printer:
"epl_zpl"
epl_zpl is the safe setting: the printer auto-detects per job, so both work.
The values that cause this symptom are epl and line_print. To set it:
printf '! U1 setvar "device.languages" "zpl"\r\n' | nc 192.168.1.157 9100
Use epl_zpl rather than zpl if anything else on your network still sends EPL.
Cause 3: something is sending it as a document
If the printer is healthy, the problem is on your side, and it is almost always that the ZPL went through a printer driver instead of straight to the printer. A driver's job is to render a document into the printer's language, so handing it a file that is already ZPL means it renders the text of your ZPL into ZPL. It does its job perfectly and you get the code on paper.
Symptoms that point here: it prints in a nice proportional font, or with margins, or you printed by opening the file and pressing Ctrl-P.
ZPL must be sent raw. Not through Word, Notepad, a print dialog, or a Windows driver queue:
# macOS / Linux
cat label.zpl | nc 192.168.1.157 9100
If you must go through a Windows queue, install the printer as a Generic / Text Only driver on a Standard TCP/IP RAW port at 9100, which passes bytes through untouched. The Zebra-branded driver will not.
Still printing text after all three?
Then check the ZPL itself before blaming the printer. A stream that never opens
with ^XA or never closes with ^XZ is not a format, and some firmware will
echo it. Paste it into the ZPL viewer: if it renders a label
there, the ZPL is valid and the problem is one of the three above.
You can also take the printer out of the question entirely. Point your app at a virtual Zebra printer, which speaks raw TCP 9100 like the real thing and shows you what arrived. If the label appears there and text appears on your desk, the difference is the printer's configuration, not your code.
Why does it come back after a power cycle?
Diagnostic mode is stored in the printer, not in the session. ~JD survives
reboots; only ~JE clears it. If the symptom returned after someone
power-cycled "to fix it", this is why.
The label prints the code and then a blank label
Normal in diagnostic mode: it dumps what it received, then feeds. It is not a second fault.
Does this affect the ~ and ^ characters specifically?
Both are the ZPL control prefixes, so if either has been remapped (~CC or
^CC changes them) the printer stops recognising your commands and treats them
as data. Rare, but it is the same class of problem, and ~JE will not fix it.
Reset the prefix or factory-default the printer.
Published 2026-09-03 · Ben Faerber, software engineer, five years building warehouse fulfillment and label-printing systems; maintainer of pdf-to-zpl (27,000+ installs powering production label generation).