Glassbox maps WASM instruction failures back to Rust source code lines using DWARF debug symbols embedded in the compiled WASM binary.
When a contract fails, Glassbox attempts to resolve the source location through the following pipeline:
- Local cache — previously resolved source is returned immediately.
- Registry — queries stellar.expert for a verified source link.
- GitHub fallback — downloads source from the linked repository when a
GitHubRetrieveris configured. --contract-sourceoverride — uses the explicitly provided local path (see below).- Interactive prompt — asks the user for a WASM path when all automatic methods fail. In non-interactive environments (CI pipelines) this stage is skipped and an explicit error is returned instead of hanging on stdin.
In CI pipelines and other non-interactive environments, the interactive prompt is disabled automatically. When all discovery stages fail, Glassbox returns an explicit error:
contract source not found: all discovery stages exhausted for contract "C..."
Stages tried: cache, registry (stellar.expert), GitHub retriever, --contract-source override
To resolve: provide --contract-source <path> pointing to the contract source directory,
or verify the contract on stellar.expert to enable registry lookup.
Use --skip-source-mapping to proceed without source mapping.
Set --skip-source-mapping to bypass source discovery entirely when you only
need raw trace output.
When automatic discovery fails (e.g. the contract is not yet verified on stellar.expert, or you are working with a private repository), you can provide the path to the contract source directory explicitly:
glassbox debug --wasm ./target/wasm32-unknown-unknown/release/my_contract.wasm \
--contract-source ./src \
<transaction-hash>Or for local WASM replay:
glassbox debug --wasm ./contract.wasm \
--contract-source /path/to/contract/srcThe --contract-source path is validated before any network or simulator work begins:
| Condition | Error |
|---|---|
| Path does not exist | --contract-source: directory not found: "<path>" |
| Path is a file, not a directory | --contract-source: "<path>" is a file, not a directory |
| Path is not accessible | --contract-source: cannot access "<path>": <os error> |
| Empty or whitespace-only value | --contract-source: value must not be empty or whitespace |
Each error includes a remediation hint so you know exactly what to fix.
- When
--contract-source <path>is set and automatic source resolution fails, Glassbox uses<path>as the root directory for resolving source file references from DWARF debug info. - The path is tried directly, then as a prefix for the relative file path reported by the DWARF info, and finally as a prefix for just the filename.
- The path is also forwarded to the simulator via
ContractSourcePathin theSimulationRequest, allowing the Rust simulator to resolve source lines during execution.
| Situation | Recommendation |
|---|---|
| Contract not verified on stellar.expert | --contract-source ./src |
| Private repository | --contract-source /path/to/repo/src |
| Monorepo with multiple contracts | --contract-source ./contracts/my_contract/src |
| CI/CD pipeline (non-interactive) | --contract-source $CONTRACT_SRC_DIR |
For best results, compile your contract with debug symbols:
# Cargo.toml
[profile.release]
debug = trueThen build:
cargo build --target wasm32-unknown-unknown --releaseSee docs/debug-symbols-guide.md for more details.
For Cargo manifests specifically, run glassbox validate-cargo to surface unsupported lto or debug values and receive actionable fixes before you build or replay a contract.
When contract sources live in another Git repository, map local path prefixes to
remote GitHub URLs in .glassbox.toml:
external_source_map = '[{"prefix":"/path/to/vendor/lib","remote_url":"https://github.com/org/lib","branch":"main"}]'Glassbox uses these mappings when a source file path falls outside the workspace repository but under the configured prefix.
For faster raw replay when you only need WASM offsets and traces:
glassbox debug --wasm ./contract.wasm --skip-source-mappingThis bypasses DWARF parsing and Git link generation in the simulator.
Control trace detail with --trace-verbosity:
| Level | Output |
|---|---|
summary |
Step names and status only |
normal |
Source locations and links (default) |
verbose |
Arguments, WASM instructions, and full event payloads |
glassbox debug --wasm ./contract.wasm --trace-verbosity summary
glassbox trace --print --trace-verbosity verbose execution.jsonWhen no DWARF symbols are available, Glassbox uses a multi-stage fallback pipeline to provide a best-effort source location:
| Stage | Mechanism | Quality |
|---|---|---|
| 1 | Full DWARF line-number tables | full |
| 2 | Partial DWARF — extract file names from .debug_line even when .debug_info is stripped |
partial |
| 3 | Symbol heuristics — infer source paths from Rust mangled symbol names | heuristic |
| 4 | Cargo manifest discovery — walk the repo for Cargo.toml files |
heuristic |
| 5 | Unknown — no mapping possible; WAT disassembly shown instead | unknown |
Each fallback stage emits a Warning: field in the result explaining what was
used and why the mapping may be inaccurate, along with a debug = true
remediation hint.
Glassbox scans target/wasm32-unknown-unknown/release/ for WASM files whose
SHA-256 hash matches the on-chain contract bytecode. When a match is found,
DWARF symbols are loaded automatically.
If the build directory is missing, Glassbox logs a debug-level message and
continues without local symbols. The message includes a suggestion to run
cargo build if local symbols are needed.
Files found in the build output directory are validated before indexing:
- Files named
.wasmbut not starting with the WASM magic bytes (\0asm) are skipped with a warning rather than silently hashed. This prevents corrupt or misnamed files (e.g. ELF binaries accidentally named.wasm) from polluting the hash table with useless entries. - Files shorter than 4 bytes cannot contain a valid magic number and are also skipped with a warning.
Example warning:
"./target/wasm32-unknown-unknown/release/old_build.wasm" does not have a valid
WASM magic number (\0asm) — skipped
Rebuild with 'cargo build --release --target wasm32-unknown-unknown'
to ensure the file is a proper WASM binary.
Any path supplied to source-mapping functions is validated for null bytes before filesystem access begins. Null bytes in file paths are a shell-injection risk and produce obscure OS errors deep in the path layer:
| Input | Error |
|---|---|
--contract-source /path\x00bad |
--contract-source: path contains null bytes and cannot be used |
--source-alias /path\x00bad.json |
--source-alias: path contains null bytes and cannot be used |
GLASSBOX_SOURCE_MAP_CACHE=/path\x00bad |
GLASSBOX_SOURCE_MAP_CACHE=... contains null bytes and cannot be used |
Each error includes a Fix: hint and the offending flag name so you know
exactly what to correct.
FallbackMapper.Resolve now guards against nil or abnormally small WASM data
(fewer than 8 bytes). Previously, passing nil data would silently fall through
all stages and return a no-context unknown result. The new behavior returns an
explicit MappingQualityUnknown result with a descriptive warning:
[sourcemap] WASM data is nil or too small (0 bytes) to contain valid content —
source location for address 0x100 cannot be resolved.
Recompile with 'cargo build --release --target wasm32-unknown-unknown'
and ensure the binary is fully uploaded.
When the local WASM binary's SHA-256 hash differs from the on-chain contract
hash, Glassbox surfaces a build mismatch warning rather than the previous
misleading opt-level mismatch message (the hash can differ for many reasons
beyond opt-level — an outdated build, a different contract, or different
compilation flags):
build mismatch: local WASM hash "a1b2c3..." does not match on-chain hash "d4e5f6..." (path: ./target/.../my_contract.wasm)
The local binary differs from the deployed contract — it may be outdated,
built with different flags, or be a completely different contract.
Hint: rebuild with 'cargo build --release --target wasm32-unknown-unknown'
and verify --opt-level matches the on-chain deployment.
When source file paths embedded in DWARF symbols don't match your local directory layout, remap them with an alias file:
glassbox debug --source-alias ./aliases.json <tx-hash>The alias file must be a flat JSON object:
{
"my_crate": "/path/to/my_crate/src",
"vendor_lib": "/path/to/vendor/lib/src"
}Validation: The file must be readable and contain valid JSON. Invalid JSON produces an explicit error, and each alias entry must have a non-empty name and non-empty target path:
--source-alias: failed to parse "<path>" as JSON: <detail>
The file must be a flat JSON object mapping alias strings to local paths.
Example: {"my_crate": "/path/to/my_crate/src"}
Alias target directories that don't exist on disk produce a warning (not an error) so debugging can continue if only some aliases are stale.
glassbox debug --dry-run validates source discovery configuration before any
simulation runs:
[OK] Source directory: ./src
[OK] Source alias file: ./aliases.json (2 mapping(s))
Warning: source-alias target for "old_crate" does not exist: "/tmp/old_crate/src"
Failures appear as numbered items in the Dry-run FAILED summary with a
Fix: hint for each.