symfony/http-foundation · error · InvalidArgumentException

The filename fallback cannot contain the "%" character.

Error message

The filename fallback cannot contain the "%" character.

What it means

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 '%'.

Solutions

  1. Remove or replace '%' in the fallback, e.g. str_replace('%', '', $fallback) before calling
  2. Do NOT pre-encode the filename — makeDisposition() rawurlencodes $filename itself for the filename* parameter
  3. Sanitize the fallback: preg_replace('/[^\x20-\x7e]/', '', $fallback) then strip '%','/','\\'
  4. Wrap in try/catch InvalidArgumentException and regenerate a sanitized fallback

Example fix

// before
$fallback = rawurlencode('résumé.pdf'); // 'r%C3%A9sum%C3%A9.pdf' → throws
HeaderUtils::makeDisposition('attachment', 'résumé.pdf', $fallback);
// after
HeaderUtils::makeDisposition('attachment', 'résumé.pdf', 'resume.pdf'); // library does the encoding
Defensive patterns

Strategy: validation

Validate before calling

$filenameFallback = str_replace('%', '', $filenameFallback);
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
    $filenameFallback = 'download';
}

Type guard

function isSafeFallback(string $s): bool {
    return (bool) preg_match('/^[\x20-\x7e]*$/', $s) && !str_contains($s, '%');
}

Try / catch

try {
    $header = HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback);
} catch (\InvalidArgumentException $e) {
    $header = HeaderUtils::makeDisposition($disposition, $filename, preg_replace('/[^\x20-\x7e]/', '', $filenameFallback));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of symfony/http-foundation@5aea19cd67 (2026-09-13). Data as JSON: /api/errors/0dc94e1d5ec5281d. Report an issue: GitHub.

Appendix: source

Thrown at HeaderUtils.php:182

     */
    public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string
    {
        if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE], true)) {
            throw new \InvalidArgumentException(\sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
        }

        if ('' === $filenameFallback) {
            $filenameFallback = $filename;
        }

        // filenameFallback is not ASCII.
        if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
            throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
        }

        // percent characters aren't safe in fallback.
        if (str_contains($filenameFallback, '%')) {
            throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
        }

        // path separators aren't allowed in either.
        if (str_contains($filename, '/') || str_contains($filename, '\\') || str_contains($filenameFallback, '/') || str_contains($filenameFallback, '\\')) {
            throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
        }

        $params = ['filename' => $filenameFallback];
        if ($filename !== $filenameFallback) {
            $params['filename*'] = "utf-8''".rawurlencode($filename);
        }

        return $disposition.'; '.self::toString($params, ';');
    }

    /**
     * Like parse_str(), but preserves dots in variable names.
     */

View on GitHub (pinned to 5aea19cd67)