thephpleague/flysystem · error · InvalidVisibilityProvided

Invalid visibility provided. Expected {$expectedMessage}, re

Error message

Invalid visibility provided. Expected {$expectedMessage}, received {$provided}

What it means

PortableVisibilityGuard::guardAgainstInvalidInput() (used when normalizing visibility settings) only accepts the exact strings Visibility::PUBLIC ('public') and Visibility::PRIVATE ('private'). Anything else — different casing, whitespace, integers, or arbitrary ACL words like 'readonly' — triggers InvalidVisibilityProvided::withVisibility(), producing 'Invalid visibility provided. Expected either Visibility::PUBLIC or Visibility::PRIVATE, received <var_export of value>'. It extends InvalidArgumentException, marking it a programming/config error rather than an I/O failure.

Source

Thrown at src/InvalidVisibilityProvided.php:18

<?php

declare(strict_types=1);

namespace League\Flysystem;

use InvalidArgumentException;

use function var_export;

class InvalidVisibilityProvided extends InvalidArgumentException implements FilesystemException
{
    public static function withVisibility(string $visibility, string $expectedMessage): InvalidVisibilityProvided
    {
        $provided = var_export($visibility, true);
        $message = "Invalid visibility provided. Expected {$expectedMessage}, received {$provided}";

        throw new InvalidVisibilityProvided($message);
    }
}

View on GitHub (pinned to b277b5dc3d)

Solutions

  1. Use the constants: League\Flysystem\Visibility::PUBLIC and League\Flysystem\Visibility::PRIVATE.
  2. Whitelist and map external input before it reaches Flysystem: in_array($v, ['public', 'private'], true) with normalization (trim + strtolower).
  3. Set default visibility in Filesystem/adapter constructors from the same constants, not free-form strings.
  4. Drop legacy octal/numeric visibility remnants from old configs — Flysystem 3+ uses string constants only.

Example fix

// before
$filesystem->write('file.txt', $data, ['visibility' => $_ENV['DEFAULT_VISIBILITY']]); // 'PUBLIC' -> throws

// after
use League\Flysystem\Visibility;

$raw = strtolower(trim($_ENV['DEFAULT_VISIBILITY'] ?? 'private'));
$visibility = $raw === 'public' ? Visibility::PUBLIC : Visibility::PRIVATE;
$filesystem->write('file.txt', $data, ['visibility' => $visibility]);
Defensive patterns

Strategy: type-guard

Validate before calling

use League\Flysystem\Visibility;

// Whitelist external input before it reaches Flysystem
$visibility = strtolower(trim($configValue));
if ( ! in_array($visibility, [Visibility::PUBLIC, Visibility::PRIVATE], true)) {
    throw new InvalidArgumentException("Visibility must be 'public' or 'private', got '{$configValue}'.");
}

Type guard

/**
 * Narrow arbitrary config/user input to a valid Flysystem visibility constant.
 */
function toFlysystemVisibility(mixed $value): ?string
{
    if ( ! is_string($value)) {
        return null;
    }
    $normalized = strtolower(trim($value));

    return in_array($normalized, ['public', 'private'], true) ? $normalized : null;
}

Try / catch

use League\Flysystem\InvalidVisibilityProvided;

try {
    $filesystem->write($path, $contents, ['visibility' => $visibility]);
} catch (InvalidVisibilityProvided $e) {
    // config/programming error: surface immediately with the config source
    throw new ConfigurationError("Bad visibility in deploy config: {$e->getMessage()}", 0, $e);
}

Prevention

When it happens

Trigger: Calling $filesystem->write($path, $contents, ['visibility' => 'Public']) or ->setVisibility($path, 'read-only'); passing an int (e.g. 1/0 or octal 0644 leftovers) so var_export prints 1; reading visibility from user input or env vars without whitelisting; default visibility configured with a misspelled constant.

Common situations: Migrating Flysystem v1/v2 code that used the old integer-ish or differently-cased visibility values; env/config values like 'PUBLIC', 'public ', or 'private-' slipping through; frontend-supplied visibility strings forwarded verbatim.

Related errors


AI-assisted analysis of thephpleague/flysystem@b277b5dc3d (2026-08-17). Data as JSON: /api/errors/160e7dfd72bb1411. Report an issue: GitHub.