Field note · 2026-07-26
A generator that writes gcode directly, checked line by line by a validator that had been passing everything. An audit found five defects. Fixing them uncovered two more. Every one of them was perfectly valid on its own and wrong only in relation to something else.
A 2.4 metre conveyor belt, printed flat, with catch-cleats along it. It came off the plate looking correct and it worked. Comparing each layer against the one below it says otherwise: at the worst layer transition 13.5 % of that layer had no material underneath it, and the belt stepped sideways 8.83 mm in a single 0.6 mm layer. That is seven bead-widths, printed into thin air.
This paragraph first said 72.9 %. That number came from sampling the lower layer down to a few hundred points, so the “nearest” material was often simply a point that had not been sampled. Hours later the same sampling error made a validator call a perfectly supported layer 94 % overhanging. It was found, fixed, and the already-published figure was never rechecked. Measured properly, by indexing the lower layer in full: 0.8 % of the belt is unsupported overall, 13.5 % at the single worst layer pair.
Two causes, and they compounded. The cleat height was a hard boolean, full height inside the belt and zero at the tension rails, so it snapped from 0 to full height between two adjacent layers: a measured 8.83 mm sideways step in one 0.6 mm layer, against a 1.2 mm bead. (The longer 4.5 m belt is built with shorter cleats and steps 6.82 mm: same defect, smaller jump.) And the cleat positions were re-chosen every layer from a test that depends on the cleat height, so as the height changed every centre slid a further 0.79 mm.
It printed because TPU is forgiving enough to be extruded into thin air. Nothing in the toolchain ever asked whether a layer had anything to stand on.
| same belt config | worst step between layers | worst layer, points unsupported |
|---|---|---|
| as shipped | 8.83 mm | 13.5 % |
| + ramp the height, fix the positions | 0.79 mm | 0.0 % |
A different generator in the same toolchain tiles a space-filling curve across the bed, 49 copies drawn as one continuous stroke. The tiles snake left-to-right, then right-to-left, so a layer ends at the far corner. The code then assumed the head was back at the near corner where the path starts.
So every layer change commanded a single move of 389.8 mm, extruding, straight across 49 finished tiles, and metered as if it had travelled 0.8 mm. The file looked fine because the file's own idea of where the head was had been reset. The machine disagreed.
The fix is not a longer number: walk the tiles in the opposite order on alternate layers, so each layer begins in the tile the last one just finished. The move is now 0.8 mm.
There was a check for the nozzle diving back down into a finished part. It compared the current
height against a layer_floor variable that is only updated by a bare Z
move, and this generator writes Z on every extruding line. A
quarter-million-line file contains about four bare Z moves.
By writing six lines of gcode that climb to Z5.1 and then extrude at Z1.5, a nozzle driven back down inside the part it just made, and running the validator on it. It passed clean.
The check had run on every file ever produced and could not fire on any of them. It was not weak. It was decorative.
Four of the guards written that day to catch these were themselves wrong on the first attempt:
| guard | what it did instead |
|---|---|
| nozzle ploughing through the layer | tested < where the damage happens at =, so it passed the exact file that had just wrecked a plate |
| starved extrusion | tracked position only from extruding moves, ignoring travels, and invented a 146.8 mm defect that does not exist |
| overhang | sampled the lower layer down to 250 points across a 315 mm plate, then called a perfectly supported layer 94 % overhanging |
| parts overlapping | measured footprints from travel moves, so the end-of-print park inflated one part's box and it reported 15 collisions that were not there |
A guard that cries wolf is not harmless either. It is the one that gets switched off, taking the real detection with it.
A guard verified only by files that pass is not verified. It is just quiet. Run it against the artifact that motivated it, and hand-write the smallest input that should fail, then check that it does.
This one arrived after the page was first published, which is why it is worth adding rather than quietly folding in: it is the most consequential of the lot, and it had been hiding behind the most ordinary-sounding symptom in 3D printing.
A print failed to bond to the plate. The obvious suspects are bed temperature, a dirty sheet, levelling, adhesive. All wrong. The generator computes how much plastic to push per millimetre once, from the body layer height, and then uses that figure for the first layer, which is deliberately printed at half that height so it squashes into the plate.
Push 0.720 mm² of plastic into a gap with room for 0.360 mm². Exactly twice what fits. The surplus is incompressible; it has nowhere to go except under the nozzle, which then ploughs the print off the plate.
The same defect was then found in a second generator at six times (0.720 mm² into 0.120 mm²), in a file whose own comment already said “layer 1 is squashed into the plate so it bonds”. The intent was written down. The arithmetic never followed it.
| generator | layer 1 was fed | reported as |
|---|---|---|
| space-filling lattice | 2.00× | “bonding failed” |
| wave ribbons | 6.00× | found by sweeping, before it was blamed on anything |
| hex lattice | 6.00× | found by sweeping |
Two separate adhesion complaints, and a third generator caught before anyone could
complain. Not one of them was adhesion. (A fourth part detached the day before for a
different over-extrusion reason: colliding fill rings, not first-layer metering. This
page previously listed it in the table above as though it were the same defect; it was not.) The rule
that came out of it is a single division, and it costs nothing to run before reaching for the
glue: commanded mm² ÷ (bead width × first layer height) should be
1.00.
A speed cap in the toolchain had been left at an old, low value, so parts were printing at just over a quarter of the intended rate (14.4 against 55 mm³/s). Raising it to the intended figure was correct, and it immediately made a generator unsafe.
That generator samples curves by angle, producing segments around 0.15 mm. Harmless when the head moves slowly. At the new speed it works out to 466 movements per second, against a controller that runs out of planning buffer near 300. It does not report an error when that happens; it simply stops moving, mid-print.
The fix is to derive the minimum segment length from the speed actually being commanded, rather than picking a number that happens to work today, so it stays correct if the cap moves again. The other generators that emit gcode were checked rather than assumed; one more (the hex lattice) had the same fault and was fixed, the rest were clean.
Not one of these is a bad line of code. Each line is correct. The belt's cleat height is right for its layer. The 390 mm move is a valid G1. The park position is a legal coordinate.
The last two are the same shape as the rest: how much plastic to push is correct for the body, and correct arithmetic applied to the wrong layer; a segment length is correct at one speed and dangerous at another. Neither number is wrong on its own.
Every defect lived in the relationship between two things. This layer and the one below, this part and its neighbour, the position the file believes in and the position the machine is actually at, the number requested and the number emitted. A checker that walks one line at a time cannot see any of them, and that is precisely the shape of a checker that reports success forever.
The generators are public: github.com/olegmlkvorg/crackle.
The validator is validate.py, and the guards described here are in it, including the
comments explaining what each one got wrong first.