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
- Use the constants: League\Flysystem\Visibility::PUBLIC and League\Flysystem\Visibility::PRIVATE.
- Whitelist and map external input before it reaches Flysystem: in_array($v, ['public', 'private'], true) with normalization (trim + strtolower).
- Set default visibility in Filesystem/adapter constructors from the same constants, not free-form strings.
- 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
- Always pass Visibility::PUBLIC / Visibility::PRIVATE constants — never raw strings or ints from env/user input.
- Normalize (trim + lowercase) and whitelist visibility values at the config boundary.
- Purge legacy octal/numeric visibility settings left over from Flysystem v1/v2 during upgrades.
- Cover visibility config with a unit test asserting the constants.
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
- Invalid stream provided, expected stream resource, received
- Unable to get checksum for $path: $reason
- ETag header not available.
- Unable to get checksum for $path: $reason
- ETag header not available.
AI-assisted analysis of thephpleague/flysystem@b277b5dc3d (2026-08-17).
Data as JSON: /api/errors/160e7dfd72bb1411.
Report an issue: GitHub.