laravelerroresarchivosstorage

Failed to open stream: No such file or directory en Laravel

Warning: include(): Failed to open stream: No such file or directory. Este error puede aparecer en varias situaciones en Laravel y tiene causas muy distintas según el contexto. Veamos las más comunes y cómo solucionarlas.

Las causas más frecuentes

Antes de ir a las soluciones específicas, las causas más habituales de este error son:

  1. Storage link no creado: Los archivos en storage/app/public no son accesibles públicamente sin el symlink
  2. Paths incorrectos: Usar rutas relativas o construirlas manualmente de forma incorrecta
  3. Caché de configuración desactualizada: El path está cacheado con un valor viejo
  4. Autoloader de Composer desactualizado: Composer no conoce una clase nueva
  5. Permisos de archivo: El servidor web no puede leer el archivo (aunque este da un error diferente)

El escenario más común es intentar acceder a archivos subidos por usuarios:

// En la migración: los archivos se guardan en storage/app/public/
Storage::disk('public')->put('avatars/user-1.jpg', $fileContents);

// Para acceder públicamente necesitas el symlink:
// public/storage → storage/app/public

Si el symlink public/storage no existe, los archivos no son accesibles desde el navegador:

# Crear el symlink
php artisan storage:link

# Verificar que existe
ls -la public/storage
# Debe mostrar: public/storage -> ../storage/app/public

En producción, si tu despliegue no ejecuta php artisan storage:link, los archivos no serán accesibles. Añádelo a tu script de despliegue.

Diferencia entre storage_path(), public_path() y base_path()

Usar el helper incorrecto es otra causa frecuente:

// base_path(): raíz del proyecto
// /var/www/html/tu-proyecto/
$path = base_path('archivo.txt');

// storage_path(): directorio storage/
// /var/www/html/tu-proyecto/storage/archivo.txt
$path = storage_path('app/public/avatars/user-1.jpg');

// public_path(): directorio public/
// /var/www/html/tu-proyecto/public/archivo.txt
$path = public_path('images/logo.png');

// resource_path(): directorio resources/
// /var/www/html/tu-proyecto/resources/views/welcome.blade.php
$path = resource_path('views/welcome.blade.php');
// MAL: Construir el path manualmente
$file = __DIR__ . '/../../storage/app/public/' . $filename; // Frágil

// BIEN: Usar los helpers de Laravel
$file = Storage::disk('public')->path($filename);

// O para verificar si existe antes de leer:
if (Storage::disk('public')->exists($filename)) {
    $contents = Storage::disk('public')->get($filename);
} else {
    // Manejar el caso de archivo no encontrado
}

La diferencia entre Storage facade y paths directos

// Usando Storage facade (recomendado)
use Illuminate\Support\Facades\Storage;

// Guardar archivo
Storage::put('archivo.txt', 'contenido');
Storage::disk('public')->put('imagen.jpg', $imageData);

// Leer archivo
$content = Storage::get('archivo.txt');

// Verificar existencia
if (Storage::exists('archivo.txt')) {
    // ...
}

// Obtener el path absoluto (para funciones PHP nativas que lo necesitan)
$absolutePath = Storage::path('archivo.txt');
// /var/www/html/tu-proyecto/storage/app/archivo.txt

// URL pública (requiere symlink para el disco 'public')
$url = Storage::disk('public')->url('imagen.jpg');
// https://tu-app.com/storage/imagen.jpg
// Usando path directo (cuando necesitas funciones PHP nativas)
$path = storage_path('app/private/report.pdf');

if (file_exists($path)) {
    return response()->download($path);
} else {
    abort(404, 'Archivo no encontrado');
}

Caché de configuración desactualizada

Cuando haces php artisan config:cache, Laravel cachea todas las configuraciones. Si cambias un path en .env o en config/filesystems.php, el caché sigue usando el valor anterior:

# El error puede aparecer porque el path está cacheado con un valor viejo
# Limpiar y regenerar el caché de configuración

php artisan config:clear
php artisan config:cache  # Para volver a cachear en producción

# Si el problema persiste, limpiar todo
php artisan optimize:clear

En desarrollo, es mejor no usar caché de configuración:

# En desarrollo, solo limpiar sin volver a cachear
php artisan config:clear
php artisan route:clear
php artisan view:clear

Error en autoloader de Composer

Si el error menciona un archivo PHP (no de datos), puede ser el autoloader:

# El autoloader no conoce una clase nueva o movida
Failed to open stream: No such file or directory in /vendor/composer/autoload_classmap.php
# Regenerar el autoloader
composer dump-autoload

# En producción, con optimización
composer dump-autoload --optimize

Debuggear paths antes de usarlos

Cuando no estás seguro de un path, añade temporalmente un log o una verificación:

public function downloadReport(Report $report)
{
    $filename = "reports/{$report->id}.pdf";
    
    // Debug: ver el path real
    \Log::debug('Intentando acceder a: ' . Storage::path($filename));
    \Log::debug('Existe: ' . (Storage::exists($filename) ? 'SI' : 'NO'));
    
    if (!Storage::exists($filename)) {
        return response()->json([
            'error' => 'Archivo no encontrado'
        ], 404);
    }
    
    return Storage::download($filename, "reporte-{$report->id}.pdf");
}

O en Tinker para explorar:

php artisan tinker

>>> storage_path()
=> "/var/www/html/mi-proyecto/storage"

>>> Storage::disk('public')->exists('avatars/1.jpg')
=> false  # El archivo no existe con ese path

>>> Storage::disk('public')->files('avatars')
=> ["avatars/user_1_avatar.jpg"]  # El nombre real es diferente

Permisos de directorio

Si el archivo existe pero sigue habiendo error, puede ser un problema de permisos:

# Ver permisos del directorio storage
ls -la storage/

# Laravel necesita que el servidor web pueda escribir en storage y bootstrap/cache
chmod -R 775 storage bootstrap/cache
chown -R www-data:www-data storage bootstrap/cache  # En Ubuntu/Debian

Manejo correcto de archivos no encontrados

Nunca asumas que un archivo existe. Siempre verifica:

public function getUserAvatar(User $user): string
{
    $avatarPath = $user->avatar; // Columna con el path guardado

    // Verificar con Storage facade
    if ($avatarPath && Storage::disk('public')->exists($avatarPath)) {
        return Storage::disk('public')->url($avatarPath);
    }

    // Devolver avatar por defecto
    return asset('images/default-avatar.png');
}

Conclusión

El error “Failed to open stream” en Laravel normalmente tiene que ver con el symlink de storage no creado (php artisan storage:link), uso incorrecto de helpers de path, o caché de configuración desactualizada. La regla general es: siempre usa la Storage facade en lugar de paths directos, siempre verifica la existencia antes de leer, y mantén tu caché limpia cuando cambias configuraciones.