thephpleague/flysystem · error · InvalidListResponseReceived
Metadata can't be parsed from item '$item' , not enough part
Error message
Metadata can't be parsed from item '$item' , not enough parts.
What it means
When listing an FTP server detected as Windows-style (the LIST line starts with a date-like pattern such as MM-DD-YY or YYYY-MM-DD), FtpAdapter::normalizeWindowsObject() collapses whitespace and splits the line into exactly 4 parts: date, time, size-or-<DIR>, and name. Fewer than 4 parts means the line is not a parseable Windows directory entry, and InvalidListResponseReceived is thrown with the offending item embedded in the message.
Source
Thrown at src/Ftp/FtpAdapter.php:408
return $this->normalizeWindowsObject($item, $base);
}
private function detectSystemType(string $item): string
{
return preg_match(
'/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/',
$item
) ? self::SYSTEM_TYPE_WINDOWS : self::SYSTEM_TYPE_UNIX;
}
private function normalizeWindowsObject(string $item, string $base): StorageAttributes
{
$item = preg_replace('#\s+#', ' ', trim($item), 3);
$parts = explode(' ', $item, 4);
if (count($parts) !== 4) {
throw new InvalidListResponseReceived("Metadata can't be parsed from item '$item' , not enough parts.");
}
[$date, $time, $size, $name] = $parts;
$path = $base === '' ? $name : rtrim($base, '/') . '/' . $name;
if ($size === '<DIR>') {
return new DirectoryAttributes($path);
}
// Check for the correct date/time format
$format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
$dt = DateTime::createFromFormat($format, $date . $time);
$lastModified = $dt ? $dt->getTimestamp() : (int) strtotime("$date $time");
return new FileAttributes($path, (int) $size, null, $lastModified);
}
private function normalizeUnixObject(string $item, string $base): StorageAttributesView on GitHub (pinned to b277b5dc3d)
Solutions
- Pin the listing format explicitly: FtpConnectionOptions::lazy('host', ..., systemType: 'unix') or 'windows' — removing auto-detection when you know the server type.
- Enable/disable 'recurseManually' or set 'useRawListContents' via a decorator to inspect raw LIST output and identify the offending line.
- If the server offers MLSD (machine listings) prefer an MLSD-capable server config, or fix the server's LIST format / chroot so it emits standard lines.
- Catch InvalidListResponseReceived around listContents() and log $e->getMessage() to capture the exact malformed item for the server admin.
Example fix
// before (auto-detection misfires on odd first line)
$options = FtpConnectionOptions::lazy('ftp.example.com');
$adapter = new FtpAdapter($options);
$adapter->listContents('/'); // throws "Metadata can't be parsed from item 'Total files: 42' , not enough parts."
// after (explicit system type)
$options = FtpConnectionOptions::lazy('ftp.example.com', '/', FTP_NATIVE, 'windows'); // 5th arg = systemType
$adapter = new FtpAdapter($options); Defensive patterns
Strategy: validation
Validate before calling
// Pin the server type instead of relying on per-connection auto-detection
use League\Flysystem\Ftp\FtpConnectionOptions;
$options = FtpConnectionOptions::lazy('ftp.example.com', '/', FTP_NATIVE, 'windows'); // explicit systemType
$adapter = new FtpAdapter($options);
// Optional: probe the raw listing before calling listContents
$raw = ftp_rawlist($connection, '/');
foreach ($raw as $line) {
if (substr_count(preg_replace('#\s+#', ' ', trim($line)), ' ') < 3) {
// non-entry line detected: filter or abort before Flysystem parses it
}
} Try / catch
use League\Flysystem\InvalidListResponseReceived;
try {
$contents = $filesystem->listContents('/')->toArray();
} catch (InvalidListResponseReceived $e) {
// message contains the exact malformed line — log it for the server admin
$this->logger->error('Unparseable FTP listing line', ['exception' => $e]);
$contents = []; // or fall back to non-recursive per-file has() checks
} Prevention
- Always set systemType explicitly in FtpConnectionOptions for known servers.
- Smoke-test listContents() during deployment to catch listing-format changes after server upgrades.
- Keep FTP servers on standard LIST output; avoid banners injected into directory listings.
When it happens
Trigger: listContents() on an FTP server whose raw LIST output contains non-entry lines (messages like 'Total files: 42', banners, blank-ish lines) that got classified as Windows format; a server whose date format accidentally matches the Windows detection regex; localized or non-standard LIST formats.
Common situations: Connecting to Windows/IIS FTP servers with custom directory-listing formats; mainframe or appliance FTP gateways that prepend status lines; servers behind FTP proxies injecting text into listings; systemType left null so detection happens per-connection based on the first line seen.
Related errors
- Unable to login/authenticate with FTP
- Could not set UTF-8 mode for connection: {host}::{port}
- Could not set passive mode for connection: {host}::{port}
- 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/188b41d73fe799da.
Report an issue: GitHub.