Skip to content

index out of bounds #73

Description

@dark-763

Bug: to-zen panics with "index out of bounds" on legacy assets re-serialized by UAssetAPI, even with zero content changes

Summary

retoc to-zen panics with index out of bounds: the len is 22 but the index is 96 inside legacy_asset.rs when converting a .uasset/.uexp pair that was extracted from a Zen container via UAssetGUI/UAssetAPI (not via retoc to-legacy), then written back out by UAssetAPI without any content changes at all. This rules out any specific edit as the cause — the panic appears to stem from a structural difference between what UAssetAPI writes for a legacy asset and what retoc's legacy parser expects.

Environment

  • Game: "Deluded" (early access, cooked with Unreal Engine 5.0 Early Access)
  • retoc version tested: both the latest GitHub release binary, and a fresh build from main (commit as of 2026-08-21) — same panic in both, only the line number shifted (1267 in release build → 1281 in fresh main build)
  • Container header version override used: --override-container-header-version Initial
  • --version UE5_0
  • Asset was originally extracted from .utoc/.ucas (Zen format) using UAssetGUI v1.1.0 (UAssetAPI commit 8b8687a), which has native Zen container reading support, opening the .utoc directly (this bypasses retoc to-legacy, which fails on this same container with an unrelated error — see "Additional context" below).

Steps to reproduce

  1. Open the game's .utoc container directly in UAssetGUI v1.1.0 (native Zen support, no retoc to-legacy involved at this step).
  2. Use Utils -> Extract all in UAssetGUI to dump all packages as legacy .uasset/.uexp pairs.
  3. Take a single extracted pair (e.g. DI_TheVialOfThirst.uasset / .uexp, a Dialogue-type asset), open it via UAssetAPI in a small C# program:
    var asset = new UAsset(path, EngineVersion.VER_UE5_0EA);
    // no property edits made at all
    asset.PackageFlags |= EPackageFlags.PKG_Cooked; // see note below
    asset.Write(outPath);
  4. Place the resulting .uasset/.uexp under <root>/SRTE/Content/SRTP/Quests/Prologue/TheVialOfThirst/.
  5. Run:
    retoc.exe --override-container-header-version Initial to-zen "<root>" "<output>" --version UE5_0
    

Actual result

[00:00:00] ----------------------------------------       0/1       SRTE/Content/SRTP/Quests/Prologue/TheVialOfThirst/DI
thread '<unnamed>' (26036) panicked at retoc\src\legacy_asset.rs:1281:51:
index out of bounds: the len is 22 but the index is 96
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Expected result

The unmodified round-tripped asset should convert back to Zen format successfully (or at least fail with a graceful error rather than a panic).

Key diagnostic finding

I first suspected this was caused by editing TextPropertyData.CultureInvariantString (localization/translation work — the replacement string differs in byte length from the original). However, I isolated the issue by running the exact same asset through UAssetAPI with zero content changes (just new UAsset() immediately followed by Write(), plus the PKG_Cooked flag fix below) — and got the identical panic (len is 22, index is 96), ruling out string length / content changes as the cause. This points to a structural/serialization mismatch between UAssetAPI's legacy writer and retoc's legacy parser (legacy_asset.rs), rather than anything specific to my edits.

Additional context: PKG_Cooked flag

Separately, I noticed that .uasset files produced by UAssetAPI's Write() (via asset.Write(path), no explicit flag manipulation) do not retain the PKG_Cooked package flag, causing:

Error: Detected absent PKG_Cooked flag in legacy package summary. Uncooked assets cannot be converted to Zen. Are you sure the asset has been Cooked?

I worked around this by explicitly doing asset.PackageFlags |= EPackageFlags.PKG_Cooked; before Write(). This may be a separate bug in UAssetAPI (not retoc), but I'm noting it here since it's part of the reproduction chain and someone else hitting the same panic will likely hit this first.

Also worth noting: retoc to-legacy fails on the original Zen container

For context on why I used UAssetGUI's native Zen reader instead of retoc to-legacy in step 1: running retoc to-legacy directly on this game's original .utoc/.ucas fails partway through with:

Failed to convert <path>.uasset: container "SRTE-WindowsNoEditor" does not contain FIoChunkId { chunk_id: "000000000000000000000007", chunk_type: LoaderInitialLoadMeta }

This happens because the game's global.utoc is unusually minimal (only 3 chunks, container_flags: 0x0, no compression/indexing). This may or may not be related to the to-zen panic above, but I'm including it in case the two are connected (e.g. both stemming from how this game's atypical global container is handled).

Files

I can provide the exact DI_TheVialOfThirst.uasset/.uexp pair (both the untouched round-trip version and the original Zen-extracted version) if useful for debugging — let me know the best way to attach them.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions