Omni.kit.usd.collect not working

The following code

import asyncio
import omni.kit.usd.collect
src = "https://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Isaac/5.1/Isaac/Environments/Grid/default_environment.usd"
dst = "~/local/copy"
collector = omni.kit.usd.collect.Collector(src, dst, flat_collection=False, usd_only=False)
asyncio.run(collector.collect())

throws an error that looks like (edited):

Task

<Task pending name='Task-10'
coro=<Collector.__open_and_analyze_layer()
running at
.../site-packages/isaacsim/kit/kernel/py/carb/profiler/__init__.py:83>
cb=[_wait.<locals>._on_completion() at
.../python3.11/asyncio/tasks.py:519]>

got Future

<Task pending name='Task-11'
coro=<_run_asyncio.<locals>.async_fn()
running at
.../site-packages/isaacsim/kit/data/Kit/Isaac-Sim/5.1/exts/3/omni.kit.usd.collect-2.4.5+69cbf6ad/omni/kit/usd/collect/async_utils.py:17>>

attached to a different loop

The code above seems to be the recommended way to use this extension. Should it be updated?

You can just run the Collect Tool from the GUI, much easier.

If you want to try to fix this code, here is some suggestions:

omni.kit.usd.collect.Collector.collect() is already integrated with Kit’s own asyncio event loop, so calling asyncio.run() from your script creates a second loop and causes the “Future attached to a different loop” error. docs.omniverse.nvidia

What’s going wrong

  • Isaac Sim / Kit starts its own asyncio loop internally (omni.kit.async_engine). forums.developer.nvidia
  • asyncio.run(collector.collect()) creates another loop, but Collector schedules work on Kit’s loop.
  • Python then complains: a task on loop A is trying to await a future that belongs to loop B → got Future <…> attached to a different loop. stackoverflow

How to fix it

Run the collector on Kit’s existing loop, not with asyncio.run. For example, in the Script Editor or in a Kit extension:

import asyncio
from omni.kit.usd.collect import Collector

src = "https://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Isaac/5.1/Isaac/Environments/Grid/default_environment.usd"
dst = "/home/USER/local/copy"  # expand ~ yourself

async def do_collect():
    collector = Collector(src, dst, flat_collection=False, usd_only=False)
    success, target_root_usd = await collector.collect()
    print("success:", success, "root:", target_root_usd)

# Use the existing loop that Kit created
loop = asyncio.get_running_loop()
loop.create_task(do_collect())

Key points:

  • Use asyncio.get_running_loop() instead of asyncio.run() inside Kit. forums.developer.nvidia
  • Schedule your coroutine with loop.create_task(...) or wrap it in whatever async helper Kit provides.
  • Expand ~ to a real path (Kit may not expand tildes in dst).

If you run this outside Kit (e.g., standalone Python with Omniverse libs), you can then use asyncio.run(do_collect()), but inside Isaac Sim / Kit you must reuse the existing event loop.

Thanks, Richard! I tried your suggestion, but the line

loop = asyncio.get_running_loop()

is throwing

RuntimeError: no running event loop

Any further help would be greatly appreciated.

Can you just run it in the app GUI?

I can’t. I’m trying to download assets programmatically during a build step so they are available later in an air-gapped, low-disk-space, environment.

Note that I am including this snippet at the top of the python script:

from isaaclab.app import AppLauncher
app_launcher = AppLauncher(headless=True)

Is this for specific assets that you need, or an entire library? We have our assets already zipped together for air-gapped download.

The zipped up folders are 150GB. We only need a tiny subset of that. We’d prefer to avoid copying 150GB during our process.

Can you take a look at this code and see if this works for you:

import asyncio
from omni.kit.usd.collect import Collector, FlatCollectionTextureOptions, DefaultPrimOnlyOptions

Basic usage

async def collect_usd_file():
“”“Collect a USD file and all its dependencies.”“”

# Create collector instance
collector = Collector(
    source_stage_url="omniverse://localhost/path/to/your/file.usd",
    target_folder="omniverse://localhost/path/to/output_folder",
    usd_only=False,  # Include non-USD assets (textures, etc.)
    flat_collection=False,  # Keep directory structure
    material_only=False,  # Collect all assets, not just materials
    texture_option=FlatCollectionTextureOptions.COPY,  _Copy textures_
    default_prim_only=False,  # Collect entire stage
    default_prim_option=DefaultPrimOnlyOptions.ROOT_LAYER_ONLY,
    convert_usda_to_usdc=False  _Keep original format_
)

# Optional: Define progress callback
def on_progress(current_step, total_steps):
    print(f"Progress: {current_step}/{total_steps}")

# Optional: Define finish callback
def on_finish():
    print("Collection complete!")
    if collector.is_finished():
        print(f"Collected to: {collector.get_target_url()}")
    collector.destroy()

# Start collection
success, collected_path = await collector.collect(
    progress_callback=on_progress,
    finish_callback=on_finish
)

return success, collected_path

Run the collection
asyncio.ensure_future(collect_usd_file())

Common Options

_Collect USD_ files only (no textures/materials)
collector = Collector(
    source_stage_url="path/to/file.usd",
    target_folder="path/to/output",
    usd_only=True  # Only USD files
)

_Flat collection (all_ files in one folder)
collector = Collector(
    source_stage_url="path/to/file.usd",
    target_folder="path/to/output",
    flat_collection=True,  # Flatten directory structure
    texture_option=FlatCollectionTextureOptions.COPY
)

_Collect only default p_rim and its dependencies
collector = Collector(
    source_stage_url="path/to/file.usd",
    target_folder="path/to/output",
    default_prim_only=True,
    default_prim_option=DefaultPrimOnlyOptions.ALL_LAYERS
)

_Convert USDA to USDC during collection_
collector = Collector(
    source_stage_url="path/to/file.usda",
    target_folder="path/to/output",
    convert_usda_to_usdc=True
)

Key Methods:

  • collector.collect() - Start collection (async)
  • collector.cancel() - Cancel ongoing collection
  • collector.is_finished() - Check if complete
  • collector.get_status() - Get current status
  • collector.get_target_url() - Get collected file path
  • collector.destroy() - Clean up when done

Thanks for the suggestion. While that code ran without issues, it did not download anything to the destination folder.

Is it fair to conclude that the omni.kit.usd.collect extension is not intended to work outside of the GUI?

It may work outside the GUI, but it is not the best approach. Having said that I do not think you need to use it this way. I am trying to give you a better workaround solution. I am trying to understand what you are needing it to do. You are building an app that you need to run air-gapped? You want to download some files from our servers? Ok so just download them as needed with USD Composer and the GUI version, make you own asset library and zip that up for air gap installation if you want. No need to download anything when building an app. Just build the app once and add the assets to it as a separate zip file.

I can certainly tell you that the collect tool is not designed to run in pure python and build time. This is a run time execution. Download what you need normally with USD Composer and just distribute your own copy of the assets you want.

I can’t get into our requirements in much detail, but at a high level, we have multiple people contributing to this asset list, and doing it over time. We need tracking via source control. Tracking zip files or folders is not easy (if two engineers make additions independently, then the last one to merge into trunk will need to merge two folders manually). Not to mention repository size, binary files, large file management, etc.

Tracking a text file containing a flat list of asset paths would be much easier. If only we could download them during build time. It is unfortunate that we can’t.

Thanks for confirming.

Ok I see where you are going. Well if this does not work during build time, an alternative is just to run a script to download everything in the assets folder you are building online, as a single standard zip file.