{"record":{"id":"eb26b2dc3ee2e14a","repo":"PHPOffice/PHPWord","slug":"failed-to-convert-password-to-ucs-2le","errorCode":null,"errorMessage":"Failed to convert password to UCS-2LE","messagePattern":"Failed to convert password to UCS-2LE","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/PhpWord/Shared/Microsoft/PasswordEncoder.php","lineNumber":125,"sourceCode":"     * @param string $password\n     * @param string $algorithmName\n     * @param string $salt\n     * @param int $spinCount\n     *\n     * @return string\n     */\n    public static function hashPassword($password, $algorithmName = self::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000)\n    {\n        $origEncoding = mb_internal_encoding();\n        mb_internal_encoding('UTF-8');\n\n        $password = mb_substr($password, 0, min(self::$passwordMaxLength, mb_strlen($password)));\n\n        //   Get the single-byte values by iterating through the Unicode characters of the truncated password.\n        //   For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.\n        $passUtf8 = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');\n        if (!is_string($passUtf8)) {\n            throw new Exception('Failed to convert password to UCS-2LE');\n        }\n\n        $byteChars = [];\n        for ($i = 0; $i < mb_strlen($password); ++$i) {\n            $byteChars[$i] = ord(substr($passUtf8, $i * 2, 1));\n\n            if ($byteChars[$i] == 0) {\n                $byteChars[$i] = ord(substr($passUtf8, $i * 2 + 1, 1));\n            }\n        }\n\n        // build low-order word and hig-order word and combine them\n        $combinedKey = self::buildCombinedKey($byteChars);\n        // build reversed hexadecimal string\n        $hex = str_pad(strtoupper(dechex($combinedKey & self::ALL_ONE_BITS)), 8, '0', \\STR_PAD_LEFT);\n        $reversedHex = $hex[6] . $hex[7] . $hex[4] . $hex[5] . $hex[2] . $hex[3] . $hex[0] . $hex[1];\n\n        $generatedKey = mb_convert_encoding($reversedHex, 'UCS-2LE', 'UTF-8');","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/PHPOffice/PHPWord/blob/aef95c04151b5633cc505f672ddd6a71da900ee1/src/PhpWord/Shared/Microsoft/PasswordEncoder.php#L107-L143","documentation":"PasswordEncoder::hashPassword() converts the UTF-8 password to UCS-2LE, a required step of the Word document-protection hashing algorithm. mb_convert_encoding() returned a non-string (per the explicit is_string() check), so the hash cannot be computed and Exception is thrown.","triggerScenarios":"hashPassword() invoked (directly or via setDocumentProtection) when mb_convert_encoding($password, 'UCS-2LE', 'UTF-8') fails — typically the mbstring extension is absent or misconfigured, or the encoding name is not supported by the installed libmbfl build.","commonSituations":"Deploying to a minimal PHP environment without ext-mbstring (composer may install a polyfill that behaves differently); unusual PHP builds lacking UCS-2LE; passwords containing malformed UTF-8 from external sources.","solutions":["Install/enable the mbstring extension (php-mbstring package) and restart the web server/CLI.","Verify mb_convert_encoding($p, 'UCS-2LE', 'UTF-8') works in your environment with a test script.","Ensure the password is valid UTF-8 before calling setDocumentProtection (run mb_check_encoding or Text::toUTF8).","If mbstring cannot be added, pre-encode with iconv('UTF-8', 'UCS-2LE', $password) as a workaround in custom code."],"exampleFix":"// before\n$protection->setDocumentProtection($passwordWithBadEncoding);\n// after\nif (!mb_check_encoding($password, 'UTF-8')) {\n    $password = mb_convert_encoding($password, 'UTF-8', 'ISO-8859-1');\n}\n$protection->setDocumentProtection($password);","handlingStrategy":"try-catch","validationCode":"if (!function_exists('mb_convert_encoding')) {\n    throw new RuntimeException('ext-mbstring required for document protection');\n}\nif (!mb_check_encoding($password, 'UTF-8')) {\n    $password = mb_convert_encoding($password, 'UTF-8', 'ISO-8859-1');\n}","typeGuard":"function isConvertibleToUcs2(string $password): bool\n{\n    return function_exists('mb_convert_encoding')\n        && is_string(mb_convert_encoding($password, 'UCS-2LE', 'UTF-8'));\n}","tryCatchPattern":"try {\n    $settings->setDocumentProtection($password);\n} catch (\\PhpOffice\\PhpWord\\Exception\\Exception $e) {\n    if (str_contains($e->getMessage(), 'UCS-2LE')) {\n        throw new RuntimeException('mbstring/UCS-2LE conversion unavailable', 0, $e);\n    }\n    throw $e;\n}","preventionTips":["Require ext-mbstring in composer.json and verify it in deployment health checks","Validate passwords are valid UTF-8 before applying document protection","Test password protection on the exact PHP build used in production"],"tags":["phpword","encoding","mbstring","password"],"backgroundTag":"missing-dependency","analyzedSha":"aef95c04151b5633cc505f672ddd6a71da900ee1","analyzedAt":"2026-09-14T10:53:58.933Z","contentChangedAt":"2026-09-14T10:53:58.933Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}