{"record":{"id":"97f6cad6f8790855","repo":"can1357/oh-my-pi","slug":"invalid-mask-mask","errorCode":null,"errorMessage":"invalid mask ${mask}","messagePattern":"invalid mask (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/utils/qrcode.ts","lineNumber":212,"sourceCode":"\t\t\tif (usedBits <= capacityBits) break;\n\t\t\tif (version >= maxVersion) {\n\t\t\t\tthrow new Error(`data too long for a QR code (${data.length} bytes, EC ${ecLevel})`);\n\t\t\t}\n\t\t}\n\n\t\tconst bits = new BitBuffer();\n\t\tbits.append(BYTE_MODE_INDICATOR, 4);\n\t\tbits.append(data.length, charCountBits(version));\n\t\tfor (const b of data) bits.append(b, 8);\n\n\t\tconst capacityBits = dataCodewords(version, ec.table) * 8;\n\t\tbits.append(0, Math.min(4, capacityBits - bits.length)); // terminator\n\t\tbits.append(0, (8 - (bits.length % 8)) % 8); // byte-align\n\t\tfor (let pad = 0; bits.length < capacityBits; pad ^= 1) bits.append(PAD_BYTES[pad]!, 8);\n\n\t\tconst codewords = QrCode.#interleave(bits.toBytes(), version, ec.table);\n\t\tconst mask = options?.mask ?? -1;\n\t\tif (mask < -1 || mask > 7) throw new Error(`invalid mask ${mask}`);\n\t\treturn new QrCode(version, ecLevel, codewords, mask);\n\t}\n\n\t/** Split into blocks, append Reed-Solomon EC, and interleave per the spec. */\n\tstatic #interleave(data: Uint8Array, version: number, ecTable: number): Uint8Array {\n\t\tconst numBlocks = NUM_EC_BLOCKS[ecTable]![version]!;\n\t\tconst eccLen = ECC_CODEWORDS_PER_BLOCK[ecTable]![version]!;\n\t\tconst rawCodewords = Math.floor(rawDataModules(version) / 8);\n\t\tconst numShort = numBlocks - (rawCodewords % numBlocks);\n\t\tconst shortLen = Math.floor(rawCodewords / numBlocks);\n\t\tconst divisor = rsDivisor(eccLen);\n\n\t\tconst blocks: Uint8Array[] = [];\n\t\tconst blockLen = shortLen + 1;\n\t\tfor (let i = 0, offset = 0; i < numBlocks; i++) {\n\t\t\tconst datLen = shortLen - eccLen + (i < numShort ? 0 : 1);\n\t\t\tconst dat = data.subarray(offset, offset + datLen);\n\t\t\toffset += datLen;","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/utils/qrcode.ts#L194-L230","documentation":"encodeBytes accepts an explicit mask option (-1 means auto-select, 0-7 per the QR spec); any other value would produce an invalid QR code, so it validates eagerly and throws 'invalid mask <n>'.","triggerScenarios":"Calling encodeBytes with options.mask set to a number outside -1..7 — e.g. 8, negative values other than -1, or NaN/garbage from unvalidated user input.","commonSituations":"Configuration plumbing passing an unparsed string or off-by-one index; UI exposing masks 1-8 instead of 0-7; user-supplied option not clamped before reaching the encoder.","solutions":["Pass mask in the range 0-7, or -1/omit it for automatic mask selection.","Clamp or validate the value at the config/UI boundary before forwarding it.","If the source is user input, parse to an integer and reject out-of-range values early with a friendly message.","Default to omitting mask entirely unless deterministic mask choice is required (e.g. tests)."],"exampleFix":"// before: encodeBytes(data, { mask: 8 });  // after: validate mask is an integer in -1..7 (clamp or reject), then encodeBytes(data, { mask });","handlingStrategy":"validation","validationCode":"function parseMask(v: unknown): number { const n = Number(v); if (!Number.isInteger(n) || n < -1 || n > 7) throw new Error(`mask must be -1..7, got ${String(v)}`); return n; }","typeGuard":"function isValidMask(v: unknown): v is number { return typeof v === \"number\" && Number.isInteger(v) && v >= -1 && v <= 7; }","tryCatchPattern":"try { const qr = encodeBytes(data, { mask: userMask }); } catch (err) { if (err instanceof Error && err.message.startsWith(\"invalid mask\")) { return encodeBytes(data); /* fall back to auto mask */ } throw err; }","preventionTips":["Clamp/validate mask at the config or UI boundary before it reaches the encoder.","Remember the valid range is -1 (auto) and 0-7 — off-by-one UIs exposing 1-8 are a common bug.","Omit mask entirely unless deterministic output (tests, reproducibility) is required.","Parse user input to an integer before forwarding option values."],"tags":["qrcode","validation","argument-error"],"backgroundTag":"invalid-parameter-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}