Troubleshooting

Printers are the least reliable component in any system that has one. This page is ordered by how often each problem actually turns up.

First, isolate the layer

Three things can be wrong and they need different fixes: your page cannot reach the bridge, the bridge cannot reach the printer, or the printer got bytes it did not understand. Establish which before changing anything.

is the bridge there?
# Which port did it take? The tray menu says, and so does this.
curl -s http://127.0.0.1:47600/printol/v1/ping

# Or sweep the range from a terminal.
for p in $(seq 47600 47619); do
  curl -s -m 1 "http://127.0.0.1:$p/printol/v1/ping" && echo " <- on $p"
done
can the OS print at all?
# macOS and Linux: what does the OS think is attached?
lpstat -p -d

# Send a byte stream straight past Printol to prove the queue is RAW-capable.
printf '\x1b@Hello\n\n\n\x1dV\x00' | lp -d EPSON_TM_T82 -o raw

If the second command produces paper and Printol does not, the problem is configuration in the app. If it does not, the problem is the queue or the printer and Printol cannot fix it.

network printers
# A network printer should accept a raw connection on 9100.
printf '\x1b@Hello\n\n\n\x1dV\x00' | nc 192.168.1.50 9100

Symptoms

Nothing answers on any port

  • Is the app running? Look for the tray or menu-bar icon; the window being closed does not stop it, but Quit does.
  • Open the ping URL directly in a browser tab. If that works and your page does not, it is a browser policy, not the bridge.
  • Safari blocks an https page fetching http://127.0.0.1. Test in Chrome or Firefox to confirm, then decide whether you need a Safari path.
  • A VPN client or endpoint-security agent that proxies loopback will break it. Pause it and retry.

The dialog never appears when pairing

  • The window opens behind a full-screen app on Windows and only flashes in the taskbar. Check there.
  • A previous request from the same origin may still be pending — only one is allowed at a time, and it expires after three minutes.
  • If the origin was previously denied, the app suppresses repeat prompts for an hour. Clear it in Settings → Paired apps.

Garbage characters instead of a receipt

  • The queue is rendering ESC/POS as text. On Windows use the “Generic / Text Only” driver, or the manufacturer’s, and never a driver that rasterises to a bitmap.
  • On macOS and Linux the job must go out with -o raw. Printol does this; check that a custom CUPS filter has not been set on the queue.
  • A USB-to-serial thermal printer may need the right baud rate set in the driver, not in Printol.

Columns do not line up

  • The paper setting does not match the roll. Print the test page — it prints a numbered ruler across the full width so you can count the true columns.
  • A code page mismatch turns ₹ or accented characters into two characters, shifting everything after them. Set the printer’s code page in Settings.
  • A font B printer at 42 columns configured as 48 will always be short. Use the ruler, not the datasheet.

The paper does not cut

  • Not every printer has a cutter, and those that do sometimes need the full cut command rather than partial. Toggle it in the printer’s settings.
  • Feed before cutting. The blade is several millimetres past the head, so cutting immediately after the last line slices through the text. The built-ins feed three lines.

The cash drawer will not open

  • The drawer is almost always wired to the printer, not the computer, so the printer must be the one to fire it.
  • Try pin 5 instead of pin 2 — both are used in the field.
  • A stiff solenoid needs a longer pulse. Raise on_ms to 100 and retry.

A4 prints but a dialog appears (Windows)

  • Silent PDF printing on Windows depends on the PDF handler. Install SumatraPDF and Printol will use it directly, with nothing on screen.
  • Otherwise the shell print verb is used, which hands the file to whatever opens PDFs — and Acrobat shows its own window.
  • Your app can always fetch the PDF from the job and print it its own way.

render_failed on a template

  • The template referenced a field your payload does not have. The message names the expression.
  • Add a fallback in the template with | default("") rather than making the field mandatory in your app.
  • Use POST /preview to iterate — it fails the same way, without wasting paper.

Developing without hardware

Add a printer with the file transport. Every job is written to disk as the exact byte stream that would have gone to the printer, which you can inspect, diff between template versions, or replay onto real hardware later.

file transport
// No printer to hand? Add one with the file transport in Settings, then:
await printol.print({ templateId: 'pos.receipt.retail', printerId: 'prn_file', data: bill });
// The exact byte stream lands in the jobs folder, ready for xxd.

Collecting a useful report

Settings → Jobs → the failed row → Copy diagnostics puts the platform, the app version, the printer configuration, the transport error and the first 64 bytes of the stream on your clipboard. It contains no invoice data. Send that with your email and the answer usually comes back the same day.