coollabsio/coolify · error · RuntimeException

Unable to write the CSV file.

Error message

Unable to write the CSV file.

What it means

RuntimeException thrown by the private writeCsvRow() helper inside the cloud:export-users Artisan command when PHP's fputcsv() returns false. fputcsv only fails when the underlying stream cannot be written to: the handle is invalid or closed, the file was opened in a non-writable mode, the disk backing the 'backups' storage disk is full, or permissions were revoked mid-run. The command opens both CSV targets with fopen($path, 'wb') on the backups disk before the loop, so a successful open followed by a failed write points at runtime I/O failure (usually disk space), not a bad path.

Source

Thrown at app/Console/Commands/Cloud/ExportUsers.php:124

        } finally {
            fclose($subscribedOutput);
            fclose($unsubscribedOutput);
        }

        $this->info("Exported {$subscribedCount} subscribed verified users to {$subscribedPath}");
        $this->info("Exported {$unsubscribedCount} unsubscribed verified users to {$unsubscribedPath}");

        return self::SUCCESS;
    }

    /**
     * @param  resource  $output
     * @param  array<int, mixed>  $fields
     */
    private function writeCsvRow($output, array $fields): void
    {
        if (fputcsv($output, $fields, ',', '"', '') === false) {
            throw new RuntimeException('Unable to write the CSV file.');
        }
    }
}

View on GitHub (pinned to 70b9acc424)

Solutions

  1. Check free space and write permission on the directory behind Storage::disk('backups') (df -h and touch on the backups path); free space if full.
  2. Verify the backups disk entry in config/filesystems.php points at a reachable, writable location and remount/fix the mount if stale, then re-run php artisan cloud:export-users.
  3. Confirm the CLI user has write access to cloud-users-subscribed.csv / cloud-users-unsubscribed.csv in that directory.
  4. If the disk is remote, fix its credentials/mount health before re-running; the command deletes and recreates the files each run so it is safe to retry.
Defensive patterns

Strategy: try-catch

Validate before calling

$dir = dirname(Storage::disk('backups')->path('cloud-users-subscribed.csv'));
if (! is_writable($dir) || disk_free_space($dir) < 1024 * 1024) {
    // abort before opening any stream: the volume is unwritable or nearly full
}

Try / catch

try {
    $this->writeCsvRow($output, $fields);
} catch (RuntimeException $e) {
    $this->error("CSV write failed mid-export: {$e->getMessage()}");

    return self::FAILURE;
}

Prevention

When it happens

Trigger: Running php artisan cloud:export-users on Coolify Cloud while (a) the filesystem behind the backups disk runs out of space as User rows stream in via lazyById(500), (b) the backups disk points at a full or unreachable remote mount (NFS/S3-compatible) that fails after fopen succeeded, or (c) the handle is closed/invalidated mid-export. Every writeCsvRow() call (header row plus one row per verified user) checks fputcsv's return and throws on the first false.

Common situations: Disk-full on the volume configured for the backups disk in config/filesystems.php; stale NFS mounts or expired S3-compatible credentials on the mount; large user tables where an external log-rotation or cleanup process touches the target files mid-export; running the command as a user whose permissions were changed after fopen.


AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17). Data as JSON: /api/errors/105c67a0b6760c5c. Report an issue: GitHub.