Start Debugging

Solución: Unable to load asset en Flutter después de agregar una imagen a pubspec.yaml

La clave del asset falta en el bundle compilado, no en tu disco. Corrige la indentación del pubspec, agrega la barra final, iguala la clave y reinicia por completo.

El archivo está en el disco, la ruta parece correcta y Flutter sigue diciendo que no puede cargarlo. Eso ocurre porque el mensaje no habla de tu disco: la clave que pasaste no está en el bundle de assets compilado. En orden de frecuencia, la razón es un bloque assets: que no está indentado bajo flutter:, una entrada de directorio a la que le falta la / final, un archivo en un subdirectorio que nunca se declaró, una clave que difiere en mayúsculas y minúsculas del nombre del archivo, o un hot reload cuando hacía falta un reinicio completo. Corrige pubspec.yaml, detén la aplicación y vuelve a ejecutarla.

======== Exception caught by image resource service ================================================
The following assertion was thrown resolving an image codec:
Unable to load asset: "assets/images/logo.png".
The asset does not exist or has empty data.

When the exception was thrown, this was the stack:
#0      PlatformAssetBundle.load (package:flutter/src/services/asset_bundle.dart:271:7)
<asynchronous suspension>
#1      AssetBundleImageProvider._loadAsync (package:flutter/src/painting/image_provider.dart:951:14)

Esta guía está escrita contra Flutter 3.44.7 y Dart 3.12.2, el canal stable al 2026-07-20. El comportamiento descrito aquí es estable desde que Flutter 3.16 cambió el formato del manifiesto de assets, y las reglas del pubspec no han cambiado en años.

Qué significa realmente el error

Image.asset('assets/images/logo.png') no abre un archivo. Entrega una clave de texto al framework, que le pide al engine los bytes registrados bajo esa clave en el bundle de assets de la aplicación. PlatformAssetBundle.load lanza la excepción en el momento en que el engine devuelve null o un búfer de longitud cero:

// flutter/lib/src/services/asset_bundle.dart, Flutter 3.44.7
throw FlutterError.fromParts(<DiagnosticsNode>[
  _errorSummaryWithKey(key),
  ErrorDescription('The asset does not exist or has empty data.'),
]);

Ese bundle lo compila una sola vez la herramienta flutter, a partir de la sección flutter: assets: de pubspec.yaml. Todo lo que esté listado ahí se copia a build/flutter_assets/ y se indexa en un manifiesto llamado AssetManifest.bin, que el engine carga al arrancar. Nada más en tu sistema de archivos existe desde el punto de vista de la aplicación en ejecución.

Así que dos cosas independientes tienen que coincidir, y el error no puede decirte cuál de las dos está mal:

  1. La declaración del pubspec tiene que meter el archivo en el bundle.
  2. La clave en tu código Dart tiene que coincidir byte por byte con la clave del bundle.

Cada causa de abajo es una de esas dos fallando.

La reproducción mínima

my_app/
  pubspec.yaml
  assets/
    images/
      logo.png
  lib/
    main.dart
# pubspec.yaml, Flutter 3.44.7
name: my_app

flutter:
  uses-material-design: true
  assets:
    - assets/images/logo.png
// lib/main.dart, Flutter 3.44.7, Dart 3.12.2
import 'package:flutter/material.dart';

void main() => runApp(
      const MaterialApp(
        home: Scaffold(
          body: Center(child: Image.asset('assets/images/logo.png')),
        ),
      ),
    );

Eso funciona. Rompe cualquier línea de ese ejemplo de las maneras que siguen y obtienes el error, sin ningún otro diagnóstico.

Causa 1: el bloque assets no está anidado bajo flutter

Esta es la falla más común y la más frustrante, porque nada se queja. flutter pub get termina bien, la compilación termina bien y la aplicación arranca con un bundle vacío.

# Wrong. Valid YAML, silently ignored.
flutter:
  uses-material-design: true
assets:
  - assets/images/logo.png

assets: en el nivel superior es una clave que la herramienta de Flutter no lee. No es un error, simplemente es configuración de otra persona en lo que respecta al parser. La forma correcta indenta assets: exactamente dos espacios bajo flutter:, con los elementos de la lista dos espacios más adentro:

# Right.
flutter:
  uses-material-design: true
  assets:
    - assets/images/logo.png

Una variante relacionada: una segunda clave flutter: más abajo en el archivo. Los mapas YAML no pueden tener claves duplicadas y, según el parser, una gana silenciosamente. Si tu pubspec creció de forma orgánica, busca en él cada aparición de flutter: en la columna cero antes de depurar cualquier otra cosa.

Causa 2: una entrada de directorio sin barra final, o un subdirectorio que nunca se declaró

Las entradas de directorio se activan una por una y no son recursivas. De la documentación de Flutter sobre cómo agregar assets: “Only files located directly in the directory are included. Resolution-aware asset image variants are the only exception. To add files located in subdirectories, create an entry per directory.”

Así que esto no declara nada útil si tus imágenes viven en assets/images/icons/:

flutter:
  assets:
    - assets/images/

y esto es lo que necesitas:

flutter:
  assets:
    - assets/images/
    - assets/images/icons/
    - assets/images/illustrations/

La barra final es lo que convierte la entrada en un directorio. - assets/images sin ella se lee como un único archivo llamado images y, como ese archivo no existe, la compilación falla a nivel de herramienta con un mensaje que sí es útil:

Error: unable to find directory entry in pubspec.yaml: /path/to/my_app/assets/images/

Vale la pena conocerlo al revés: si tu compilación tuvo éxito y aun así obtienes Unable to load asset en tiempo de ejecución, la entrada coincidió con algo. El problema entonces es una clave que no coincide, no una declaración faltante.

La única excepción a la regla de no recursividad son las variantes según resolución. Si declaras assets/images/logo.png, entonces assets/images/2.0x/logo.png y assets/images/3.0x/logo.png se empaquetan automáticamente y AssetImage elige la correcta según el device pixel ratio. Nunca declaras tú mismo los directorios de variantes.

Causa 3: la clave del código no coincide con la clave del bundle

Las claves del bundle son cadenas exactas. Tres maneras en que se desvían de lo que escribiste:

Mayúsculas y minúsculas. Tu máquina de desarrollo casi con certeza tiene un sistema de archivos insensible a mayúsculas (APFS en macOS por defecto, NTFS en Windows). Image.asset('assets/images/Logo.png') resuelve un archivo llamado logo.png localmente y falla en un dispositivo Android, en iOS, en web y en cualquier runner de CI con Linux. Si una compilación funciona en tu laptop y falla en todas partes, revisa esto primero. Es la explicación más probable para un caso de mismo código con resultado distinto según la máquina.

Un ./ inicial o un espacio perdido. './assets/images/logo.png' es una cadena distinta de 'assets/images/logo.png', y el bundle solo contiene la segunda. Un espacio en blanco al final dentro de un valor YAML entre comillas tiene el mismo efecto.

El prefijo packages/. Un asset que viene dentro de un paquete del que dependes se identifica como packages/<package_name>/<path>, con el directorio lib/ del paquete implícito y nunca escrito. Para cargar lib/assets/bg.png desde un paquete llamado fancy_backgrounds:

// Flutter 3.44.7. Either form works; they produce the same key.
Image.asset('packages/fancy_backgrounds/assets/bg.png');
Image.asset('assets/bg.png', package: 'fancy_backgrounds');

Si tú escribiste el paquete, también tiene que declarar esos archivos en su propio pubspec.yaml. Los assets de una dependencia no se empaquetan solo porque el archivo exista en .pub-cache.

Causa 4: hiciste hot reload cuando necesitabas reiniciar

El hot reload intercambia código Dart dentro de un isolate en ejecución. El bundle de assets y su manifiesto los produce la herramienta cuando se lanza la aplicación. Editar pubspec.yaml para agregar una entrada nueva cambia el manifiesto, y una aplicación en ejecución conserva el manifiesto con el que arrancó.

Detén la sesión y vuelve a iniciarla. Ni r, ni R:

# Flutter 3.44.7
# Ctrl-C to end the current run, then:
flutter run

Cambiar los bytes de un asset que ya está declarado sí se vuelve a empaquetar en el reload y no necesita esto. Cambiar el conjunto de assets declarados sí.

Causa 5: salida obsoleta en el disco

Rara vez es la causa, es barato descartarla y es lo primero que te dice toda respuesta en internet, razón por la cual se la culpa de muchas más fallas de las que produce. Sí es una causa real en iOS, donde un bundle .app actualizado a medias puede sobrevivir a una recompilación:

# Flutter 3.44.7
flutter clean
flutter pub get
flutter run

Si lo que falla en el camino es el propio flutter pub get, se trata de un problema de resolución de dependencias y no de assets, y la salida del solucionador de restricciones es un ejercicio aparte: mira cómo leer un error de version solving failed en pubspec.yaml.

Deja de adivinar: imprime las claves que están realmente en el bundle

Cada sección anterior es una hipótesis. Puedes reemplazarlas todas con una sola medición. AssetManifest es la API compatible para leer el manifiesto en tiempo de ejecución, agregada cuando AssetManifest.json fue reemplazado por AssetManifest.bin:

// Flutter 3.44.7, Dart 3.12.2
import 'package:flutter/services.dart';

Future<void> dumpAssetKeys() async {
  final manifest = await AssetManifest.loadFromAssetBundle(rootBundle);
  for (final key in manifest.listAssets()..sort()) {
    debugPrint(key);
  }
}

Llámala desde main detrás de una comprobación kDebugMode y lee la consola. Lo que se imprima es lo que el engine puede servir. Si tu ruta no está, el problema es la Causa 1 o la 2. Si está presente algo casi idéntico a tu ruta, es la Causa 3, y la diferencia entre las dos cadenas es tu solución.

No parsees AssetManifest.bin por tu cuenta. Flutter lo documenta como un detalle de implementación cuyo formato puede cambiar sin aviso, y AssetManifest.json ya no se genera en absoluto, así que el código que todavía llama a rootBundle.loadString('AssetManifest.json') lanza exactamente este error con AssetManifest.json como clave.

También puedes inspeccionar el bundle sin ejecutar nada:

# Flutter 3.44.7. Writes the bundle the engine would load.
flutter build bundle
ls build/flutter_assets/assets/images/

# Or check what shipped inside a built APK:
unzip -l build/app/outputs/flutter-apk/app-debug.apk | grep flutter_assets

Variantes que caen en esta página

La idea de fondo: este error es un fallo de búsqueda en una estructura de datos que produjo tu compilación. Trátalo así. Imprime listAssets(), compara la cadena que pasaste contra las cadenas que existen, y la solución siempre está en uno de los dos lados de esa comparación.

Relacionados

Fuentes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Volver