bevyengine/bevy · error · WorldAssetLoaderError

Could not parse RON: {0}

Error message

Could not parse RON: {0}

What it means

After the bytes are read, WorldAssetLoader parses the .world asset as RON (Rusty Object Notation). Any parse failure becomes WorldAssetLoaderError::RonSpannedError with "Could not parse RON: {0}", where ron::error::SpannedError embeds the exact line/column span of the syntax problem. Structurally valid but semantically wrong data (e.g. missing struct fields) can also surface here as RON/serde deserialization errors.

Source

Thrown at crates/bevy_world_serialization/src/world_asset_loader.rs:45

impl FromWorld for WorldAssetLoader {
    fn from_world(world: &mut World) -> Self {
        let type_registry = world.resource::<AppTypeRegistry>();
        WorldAssetLoader {
            type_registry: type_registry.0.clone(),
        }
    }
}

/// Possible errors that can be produced by [`WorldAssetLoader`]
#[cfg(feature = "serialize")]
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum WorldAssetLoaderError {
    /// An [IO Error](std::io::Error)
    #[error("Error while trying to read the world file: {0}")]
    Io(#[from] std::io::Error),
    /// A [RON Error](ron::error::SpannedError)
    #[error("Could not parse RON: {0}")]
    RonSpannedError(#[from] ron::error::SpannedError),
}

#[cfg(feature = "serialize")]
impl AssetLoader for WorldAssetLoader {
    type Asset = DynamicWorld;
    type Settings = ();
    type Error = WorldAssetLoaderError;

    async fn load(
        &self,
        reader: &mut dyn Reader,
        _settings: &(),
        load_context: &mut LoadContext<'_>,
    ) -> Result<Self::Asset, Self::Error> {
        let mut bytes = Vec::new();
        reader.read_to_end(&mut bytes).await?;
        let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;

View on GitHub (pinned to 396ca72708)

Solutions

  1. Read the span in the error message — RON reports the exact line and column to fix.
  2. Round-trip a minimal world through your exporter to confirm the current format, and diff it against the failing file.
  3. Re-export the world with the same Bevy/version and feature set as the loading app.
  4. Validate edited files with a RON parser (ron::from_str on the string, or an editor RON plugin) before shipping.
  5. Resolve merge conflicts in .world files by re-exporting rather than hand-merging.

Example fix

# before: assets/worlds/level1.world (missing closing paren — span error points here)
(
    entities: {},
    resources: {}

# after
(
    entities: {},
    resources: {},
)
Defensive patterns

Strategy: try-catch

Validate before calling

// validate RON in tooling or tests before shipping the asset
fn ron_parses(source: &str) -> bool {
    ron::from_str::<ron::value::Value>(source).is_ok()
}

Try / catch

match asset_server.get_load_state(&world_handle) {
    Some(LoadState::Failed(err)) => {
        if let bevy_asset::AssetLoadError::AssetLoaderError(loader_err) = &*err {
            if let Some(WorldAssetLoaderError::RonSpannedError(spanned)) =
                loader_err.error.downcast_ref::<WorldAssetLoaderError>()
            {
                // spanned.code / span carry the exact line & column to fix
                error!("bad RON at {:?}: {}", spanned.span, spanned.code);
            }
        }
    }
    _ => {}
}

Prevention

When it happens

Trigger: Hand-editing a .world file and breaking syntax (unbalanced parentheses, missing commas, bad escapes); a world exported by a different Bevy/version or with different component types than the loading app expects; line-ending or encoding corruption; feeding JSON or another format to the RON parser.

Common situations: Level designers hand-tweaking serialized worlds; version drift between exporter and importer; merge conflicts in checked-in .world files; tooling that writes JSON into .world files.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/1e5f49d1f5dc545f. Report an issue: GitHub.