{"record":{"id":"0dc94e1d5ec5281d","repo":"symfony/http-foundation","slug":"the-filename-fallback-cannot-contain-the-character","errorCode":null,"errorMessage":"The filename fallback cannot contain the \"%\" character.","messagePattern":"The filename fallback cannot contain the \"%\" character\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"HeaderUtils.php","lineNumber":182,"sourceCode":"     */\n    public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string\n    {\n        if (!\\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE], true)) {\n            throw new \\InvalidArgumentException(\\sprintf('The disposition must be either \"%s\" or \"%s\".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));\n        }\n\n        if ('' === $filenameFallback) {\n            $filenameFallback = $filename;\n        }\n\n        // filenameFallback is not ASCII.\n        if (!preg_match('/^[\\x20-\\x7e]*$/', $filenameFallback)) {\n            throw new \\InvalidArgumentException('The filename fallback must only contain ASCII characters.');\n        }\n\n        // percent characters aren't safe in fallback.\n        if (str_contains($filenameFallback, '%')) {\n            throw new \\InvalidArgumentException('The filename fallback cannot contain the \"%\" character.');\n        }\n\n        // path separators aren't allowed in either.\n        if (str_contains($filename, '/') || str_contains($filename, '\\\\') || str_contains($filenameFallback, '/') || str_contains($filenameFallback, '\\\\')) {\n            throw new \\InvalidArgumentException('The filename and the fallback cannot contain the \"/\" and \"\\\\\" characters.');\n        }\n\n        $params = ['filename' => $filenameFallback];\n        if ($filename !== $filenameFallback) {\n            $params['filename*'] = \"utf-8''\".rawurlencode($filename);\n        }\n\n        return $disposition.'; '.self::toString($params, ';');\n    }\n\n    /**\n     * Like parse_str(), but preserves dots in variable names.\n     */","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/symfony/http-foundation/blob/5aea19cd678fa4140f6108406f1096de5e9ed6e4/HeaderUtils.php#L164-L200","documentation":"Percent characters are rejected in $filenameFallback because '%' is not safe in HTTP headers — it could be confused with percent-encoding (RFC 2047/5987 escape sequences) and manipulated, so makeDisposition() throws this InvalidArgumentException when the fallback contains '%'.","triggerScenarios":"Calling makeDisposition($disposition, $filename, $filenameFallback) where $filenameFallback contains '%' — e.g. a fallback of '100% report.pdf', or pre-percent-encoded filenames like 'r%C3%A9sum%C3%A9.pdf' passed as the fallback.","commonSituations":"Developers pre-rawurlencode() the filename themselves and pass the encoded string as the fallback (double-encoding attempt); filenames containing literal '%' from user uploads (e.g. '50% off.pdf') without sanitizing the fallback.","solutions":["Remove or replace '%' in the fallback, e.g. str_replace('%', '', $fallback) before calling","Do NOT pre-encode the filename — makeDisposition() rawurlencodes $filename itself for the filename* parameter","Sanitize the fallback: preg_replace('/[^\\x20-\\x7e]/', '', $fallback) then strip '%','/','\\\\'","Wrap in try/catch InvalidArgumentException and regenerate a sanitized fallback"],"exampleFix":"// before\n$fallback = rawurlencode('résumé.pdf'); // 'r%C3%A9sum%C3%A9.pdf' → throws\nHeaderUtils::makeDisposition('attachment', 'résumé.pdf', $fallback);\n// after\nHeaderUtils::makeDisposition('attachment', 'résumé.pdf', 'resume.pdf'); // library does the encoding","handlingStrategy":"validation","validationCode":"$filenameFallback = str_replace('%', '', $filenameFallback);\nif (!preg_match('/^[\\x20-\\x7e]*$/', $filenameFallback)) {\n    $filenameFallback = 'download';\n}","typeGuard":"function isSafeFallback(string $s): bool {\n    return (bool) preg_match('/^[\\x20-\\x7e]*$/', $s) && !str_contains($s, '%');\n}","tryCatchPattern":"try {\n    $header = HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback);\n} catch (\\InvalidArgumentException $e) {\n    $header = HeaderUtils::makeDisposition($disposition, $filename, preg_replace('/[^\\x20-\\x7e]/', '', $filenameFallback));\n}","preventionTips":["Never pre-urlencode filenames — the library encodes $filename itself","Sanitize fallbacks by removing '%', '/', and '\\\\' before calling","Validate user-supplied filenames against a safe-character whitelist"],"tags":["php","http-headers","filename","invalid-argument","symfony"],"backgroundTag":"invalid-argument-value","analyzedSha":"5aea19cd678fa4140f6108406f1096de5e9ed6e4","analyzedAt":"2026-09-13T01:52:22.855Z","contentChangedAt":"2026-09-13T01:52:22.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}