Skip to main content

debug-operation

Triage a failing Usd Optimize operation. Use when an op errors, silently no-ops, or returns unexpected output.

Source facts

Repository
NVIDIA-Omniverse/usd-optimize
Last source activity
October 1, 2026 at 01:28
Detected SKILL.md language
English
Stars
36
Forks
7

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
debug-operation
description
Triage a failing Usd Optimize operation. Use when an op errors, silently no-ops, or returns unexpected output.
allowed-tools
Shell, Read, Grep, Glob
metadata
{"author":"NVIDIA Corporation","version":"1.0.1","tags":["debug","troubleshooting","operations"]}
<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. --> <!-- SPDX-License-Identifier: Apache-2.0 --> # Debug Operation Structured workflow for triaging an operation that fails, produces unexpected output, or silently does nothing. ## What this skill covers Search this doc for keywords like `verbose`, `analysisMode`, `argument key`, `silent no-op`, `mesh-less`, `instanced`, `libusd` to jump. - **Step 1** — reproduce and capture the log. - **Step 2** — check the basics (argument keys, input stage). - **Step 3** — enable verbose + analysis mode. - **Step 4** — diagnose by failure pattern (tables for common failure modes). - **Operation-family cheat sheet** — per-family gotchas. Companion skills: `run-operations` (how to invoke), `tune-parameters` (when the op runs but the output isn't right), `build` (when the build itself is broken). --- ## Step 1 — Reproduce and capture the log Run the operation through the `usdOptimize` CLI with full logging, and omit `-w` so no (bad) output file is written. ```bash # POSIX — verbose, capture stats, no output written BIN=_build/linux-x86_64/release/bin/usdOptimize "$BIN" -i "$ASSET" -o <key> -a <arg>=<val> -v -s > "$TMPDIR/debug-run.log" 2>&1 ``` `-v` is verbose and `-s` captures before/after stats. Omitting `-w` means nothing is written. The log captures the C++ `[INFO]`/`[WARN]`/`[ERROR]` lines. For a multi-op chain, use `-c <config.json>` instead of `-o`/`-a`. **Do not add `-r` here.** `-r` diverts the operation's warnings into the report file instead of stdout, so `[WARNING]` lines the checks below grep for never reach your log. If you also want the report, run a second pass with `-r` added rather than folding it into this one. If the user already has a log (e.g. from a prior `run-operations` run), skip this step and read their log directly. ## Step 2 — Check the basics Most "operation does nothing" reports fall into one of three categories. Check them in order before going deeper. ### 2a. Argument key mismatch An unknown key in a `-c` config logs `[WARNING] Unknown argument '<key>' for operation '<op>' -- ignoring it` and the operation runs with the default, so grep the log for `Unknown argument` first. (Via `-a` it's a hard error instead: `Error: invalid argument specified`, exit 1.) Verify every key in the user's config matches the operation's `addArgument()` declarations: ```bash # Check the C++ source for the canonical argument keys rg 'addArgument' source/operations/<key>/ --glob '*.cpp' --glob '*.h' ``` Or check the argument list in `docs/operations/<key>.rst`. Common mistakes: - Camel-case vs snake-case (`mergeVertices` not `merge_vertices`). - Plural vs singular (`paths` not `path`, `meshPrimPaths` not `meshPrimPath`). - Boolean as string (`true` not `"true"` in JSON). ### 2b. Wrong operation key The operation key is the first string argument to the `Operation(...)` base constructor, not the class name or the `USD_OPTIMIZE_PLUGIN_INIT` argument. Verify: ```bash rg 'Operation\(' source/operations/<key>/ --glob '*.cpp' -A1 | head -5 ``` ### 2c. Input stage has no matching prims Many operations scope work via a `paths` argument or operate only on specific prim types. If the stage has no prims matching the filter, the operation returns success but does nothing. ```python # Quick check: does the stage have meshes? from pxr import Usd, UsdGeom stage = Usd.Stage.Open("<asset>") meshes = [p for p in stage.TraverseAll() if p.IsA(UsdGeom.Mesh)] print(f"{len(meshes)} meshes") ``` ## Step 3 — Enable verbose and analysis mode If the basics check out, run in analysis mode to see what the operation *would* do without mutating the stage: ```python from usd_optimize.core import ExecutionContext, UsdOptimizeCore from pxr import Usd, UsdUtils stage = Usd.Stage.Open("<asset>") context = ExecutionContext() context.usdStageId = UsdUtils.StageCache.Get().Insert(stage).ToLongInt() context.analysisMode = 1 context.verbose = 1 success, error, output = UsdOptimizeCore.getInstance().executeOperation( "<key>", context, {<args>} ) print("success:", success) print("error:", error) print("output:", output) ``` Not all operations support analysis mode. Check: ```bash rg 'getSupportsAnalysis' source/operations/<key>/ --glob '*.cpp' ``` If the operation doesn't override `getSupportsAnalysis`, analysis mode is unavailable — fall back to running on a disposable copy of the stage. For the full CLI flag reference (`-v`, `-s`, `-an`, `-r`, `-st`), see `docs/cli.rst`. ## Step 4 — Diagnose by failure pattern ### Hard failures (operation returns error) | Log pattern | Likely cause | Fix | |---|---|---| | `Stage not found` or `Invalid stage id` | `ExecutionContext.usdStageId` not set or stale. | Use `context.set_stage(stage)` or set `usdStageId` via `UsdUtils.StageCache`. | | `Operation '<key>' not found` | Operation plugin didn't load — either not built or not on the plugin search path. | Rebuild (`./repo.sh build`). Check `UsdOptimizeCore.getInstance().getOperations()` for the registered list. | | `libusd` mismatch / stage-cache miss (see `Operation.cpp` error) | Two copies of `libusd` loaded — Python's `pxr` resolves to a different one than the C++ core. | Use the build's bundled Python via the wrapper scripts. See `run-validators` § CLI invocation for the `libusd` alignment exports. | | Crash / segfault during execution | Usually a real bug. | Isolate the failing prim path, write a minimal repro, and file against the operation. | ### Silent no-ops (operation returns success, nothing changed) | Pattern | Likely cause | Fix | |---|---|---| | Operation produces **no log line at all** | `paths` matched nothing. No operation prints a "0 processed" line, so silence *is* the signal — and the exit code is still 0. | Check the `paths` argument (see the bare-path rule below). An empty `paths` array means "all prims" for most ops, but some interpret it as "nothing selected." | | `paths` set to a bare non-Mesh prim path | A bare path matches **only that one prim**. `paths=/World` on an Xform therefore matches nothing, silently, with exit 0 and no output. | Use the SdfPathExpression descendant form `/World//*`, or list both (`["/World", "/World//*"]`) when the target may itself be the Mesh. | | Operation ran but output is identical to input | Arguments are at defaults that produce no change (e.g. `reductionFactor: 100` keeps everything). | Review the parameter defaults in the guide or C++ source. | | Log shows processing but the save failed | No `-w`/`--write` output path was given, or `stage.GetRootLayer().Save()` / `.Export()` wasn't called. | Check the runner output for the save step. (There is no `--no-save` flag — omitting `-w` is how you run without writing.) | ### Unexpected output (operation ran but result is wrong) | Pattern | Likely cause | Fix | |---|---|---| | Only some prims were affected | Operation scoped by `paths` or a prim-type filter. | Widen `paths` or check `meshPrimPaths` / similar scope arguments. | | Values look wrong (e.g. extreme decimation) | World-unit mismatch — parameter values are in stage units, and `metersPerUnit` doesn't match assumptions. | Check `UsdGeom.GetStageMetersPerUnit(stage)` and scale parameters accordingly. | | Merge produced unexpected grouping | `mergePoint` / `rootPath` / `considerMaterials` settings. | See `docs/operations/merge.rst`. | --- ## Operation-family cheat sheet ### Mesh operations (`meshCleanup`, `decimateMeshes`, `merge`, etc.) - Require `UsdGeomMesh` prims. No meshes = no work. - `merge` won't merge a boundary with a single mesh unless `allowSingleMeshes: true`. - `decimateMeshes` can't reduce below the topological minimum per mesh. Assets with many small parts will overshoot the target percentage. - `reductionFactor` is a percentage (0–100), not a fraction. `0.5` means "keep 0.5%", not "keep 50%". ### Deduplication (`deduplicateGeometry`, `deduplicateHierarchies`) - Prims with existing references/payloads are excluded from grouping. - Instanced prims can't be authored in place — deinstance first. - `deduplicateHierarchies` traverses from the stage's default prim. No default prim = no work. - Material-related prims (`Material`, `Shader`, `NodeGraph`, `Looks`, `Materials` scopes) are intentionally skipped. ### Analysis-only operations (`findCoincidingGeometry`, `findFlatHierarchies`, `findOverlappingMeshes`, `findOccludedMeshes`) - These detect but don't fix. The "fix" is a separate operation (e.g. `removePrims`, `flattenHierarchy`). - Results come back in `output["analysis"]` — inspect that dict. ### Material operations (`optimizeMaterials`) - `optimizeMaterialsMode: 1` ("Convert to color") replaces material networks with flat colors. Unintended if the user didn't ask for it. The mode is an enum, not a boolean: `0` Deduplicate (the default), `1` Convert to color, `2` Remove unbound, `3` Deduplicate with primvars. ### Hierarchy operations (`flattenHierarchy`, `pruneLeaves`) - These operate on the prim hierarchy, so they run on mesh-less stages too. - `flattenHierarchy` has params that control depth; defaults may not flatten enough. --- ## See also - `docs/operations/<key>.rst` — per-operation parameter reference and tuning guidance. - `docs/cli.rst` — CLI flags (`-v`, `-s`, `-an`, `-r`, ...). - `.agents/skills/run-operations/SKILL.md` — errors table for driver failures. - `.agents/skills/tune-parameters/SKILL.md` — when the operation runs but output quality needs iteration. ## Purpose Provide a structured triage workflow for an Usd Optimize operation that fails, silently no-ops, or produces unexpected output. Unifies log reading, argument verification, verbose / analysis-mode invocation, and a per-family cheat-sheet of common failure patterns so the agent can move from a vague "it didn't work" report to a concrete root cause without guessing. ## Prerequisites - A built repo (`./repo.sh build` or `repo.bat build`) — verbose / analysis-mode invocation requires the bundled Python. - The operation key (e.g. `meshCleanup`) and the user's invocation — config JSON, command line, or Python snippet — so the actual arguments are known. - A reproducible invocation. If the failure is intermittent, capture a log first with `--verbose -s` and no `-w` (there is no `--no-save` or `--captureStats` flag; `-s`/`--stats` is the stats switch). ## Limitations - This skill does not patch C++ source — its output is a diagnosis, not a code fix. For changes to the operation itself, the user works in `source/operations/<key>/` directly. - Analysis mode is unavailable for operations that don't override `getSupportsAnalysis()`. The skill falls back to running on a disposable stage copy in those cases. - Crashes / segfaults are surfaced as bug reports — the skill won't attach a debugger or symbolicate stacks. ## Troubleshooting This whole skill *is* a troubleshooting guide for Usd Optimize operations. The table below covers the meta-failure modes — when this skill itself can't make progress. | Symptom | Likely cause | Fix | |---|---|---| | Verbose log is empty | `executionContext` step missing or placed after the failing op. | Put `{"operation":"executionContext","verbose":1}` as the first config entry. **Use `1`, not `true`** — the parser reads these keys with `IsInt()`, so a JSON boolean is silently ignored. `captureStats` is not a recognised key here; use the CLI `-s` instead. | | Analysis mode reports `not supported` | Operation doesn't implement `executeAnalysisImpl`. | Drop analysis mode; run on a copy of the stage and omit `-w` so nothing is written. | | Op succeeds standalone but fails in chain | Earlier step in the chain mutated the stage in a way that invalidates the inputs. | Bisect the chain — run the failing op directly after the input is loaded. | | `libusd` mismatch error from `Operation.cpp` | Two `libusd` builds loaded — typical with system `pxr` + dev-tree Usd Optimize. | Use the build's bundled Python via the wrapper scripts (`tools/perf_*/run.sh`). See `run-validators` § CLI invocation for the `libusd` alignment exports. |
View on GitHub