{"record":{"id":"873222b5fe89052a","repo":"ramsey/uuid","slug":"length-must-be-an-even-number","errorCode":null,"errorMessage":"Length must be an even number","messagePattern":"Length must be an even number","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Generator/CombGenerator.php","lineNumber":83,"sourceCode":"        private NumberConverterInterface $numberConverter\n    ) {\n    }\n\n    /**\n     * @throws InvalidArgumentException if $length is not a positive integer greater than or equal to CombGenerator::TIMESTAMP_BYTES\n     *\n     * @inheritDoc\n     */\n    public function generate(int $length): string\n    {\n        if ($length < self::TIMESTAMP_BYTES) {\n            throw new InvalidArgumentException(\n                'Length must be a positive integer greater than or equal to ' . self::TIMESTAMP_BYTES\n            );\n        }\n\n        if ($length % 2 !== 0) {\n            throw new InvalidArgumentException('Length must be an even number');\n        }\n\n        $hash = '';\n\n        /** @phpstan-ignore greater.alwaysTrue (TIMESTAMP_BYTES constant could change in child classes) */\n        if (self::TIMESTAMP_BYTES > 0 && $length > self::TIMESTAMP_BYTES) {\n            $hash = $this->generator->generate($length - self::TIMESTAMP_BYTES);\n        }\n\n        $lsbTime = str_pad(\n            $this->numberConverter->toHex($this->timestamp()),\n            self::TIMESTAMP_BYTES * 2,\n            '0',\n            STR_PAD_LEFT,\n        );\n\n        return (string) hex2bin(str_pad(bin2hex($hash), $length - self::TIMESTAMP_BYTES, '0') . $lsbTime);\n    }","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/ramsey/uuid/blob/da5b521600a707d2dd097598464bd3090de850f5/src/Generator/CombGenerator.php#L65-L101","documentation":"CombGenerator::generate() requires an even byte length (InvalidArgumentException otherwise) because it assembles the value from hexadecimal pairs - the timestamp is produced as hex and padded to TIMESTAMP_BYTES * 2 characters before being packed back to bytes.","triggerScenarios":"$combGenerator->generate(15) or any odd length; computing length dynamically (e.g. strlen-derived) and passing an odd result.","commonSituations":"Configurable ID sizes computed from user input or column sizes; porting token code that assumed odd sizes were fine; combining this check's sibling error (length < 6) during refactors.","solutions":["Round the length: $length = (int) ceil($length / 2) * 2; and keep it >= 6.","Validate size configuration at boot so odd lengths never reach generate().","Replace COMB usage with Uuid::uuid7() to drop the constraint entirely."],"exampleFix":"// before\n$bytes = $combGenerator->generate(15); // odd -> InvalidArgumentException\n\n// after\n$length = max(6, (int) ceil(15 / 2) * 2); // 16\n$bytes = $combGenerator->generate($length);","handlingStrategy":"validation","validationCode":"$length = max(6, (int) ceil($requestedLength / 2) * 2); // even and >= 6\n$bytes = $combGenerator->generate($length);","typeGuard":"function isValidCombLength(int $length): bool\n{\n    return $length >= 6 && $length % 2 === 0;\n}","tryCatchPattern":null,"preventionTips":["Round computed lengths to the next even number before calling generate().","Keep length constants (not formulas) for ID sizes in config.","Test boundary values (5, 6, 7, 15, 16) against your length helper.","Adopt UUIDv7 to eliminate COMB length rules entirely."],"tags":["php","ramsey-uuid","comb-generator","generator-length","even-length"],"backgroundTag":"generator-invalid-length","analyzedSha":"da5b521600a707d2dd097598464bd3090de850f5","analyzedAt":"2026-08-21T01:35:29.252Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}