paritytech / paritytech/polkadot-cli
`--json` outputs invalid JSON (`undefined`) for empty storage values
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 10
- Forks
- 2
- Avg merge
- 12h 35m
- Merged PRs (30d)
- 4
Description
Problem
Storage queries and runtime API calls return the literal string undefined when there is no value — not null, not valid JSON. This breaks any jq or JSON-consuming pipeline.
$ dot asset-hub.apis.AssetConversionApi.get_reserves "$NATIVE" "$ASSET" --json
undefined
# Breaks with: jq: parse error: Invalid numeric literal
echo "$RESERVES" | jq '.[0]'
Impact
Every script needs a defensive guard before any jq call:
if [ "$RESERVES" == "undefined" ] || [ "$RESERVES" == "null" ]; then ...
Three-Way Semantics
There are three distinct states in Substrate storage, and dot correctly distinguishes them:
| Substrate state | dot output |
Example |
|---|---|---|
| Key exists, has value | {...} / "..." |
Assets.Account for funded account |
Key exists, value is None |
null |
Game.Nfts entry (key present, Option is None) |
| Key doesn't exist at all | undefined |
Assets.Account for unknown address |
Collapsing undefined into null would lose the distinction between "key not found" and "key exists but maps to None" — which matters for pallets where null values are meaningful.
Root Cause
formatJson() in src/core/output.ts calls JSON.stringify(data, replacer, 2). When data is JS undefined, JSON.stringify(undefined) returns the JS value undefined (not a string), and console.log(undefined) prints the literal string "undefined" to stdout — which is not valid JSON.
Additionally, formatPretty() has an explicit fallback that returns the string "undefined".
Suggested Fix
Keep the three-way semantics, but make --json output always valid JSON. Options:
Option A: Envelope
Wrap in a result object when --json is passed:
{"found": true, "value": {"balance": "1000"}} // key exists with value
{"found": true, "value": null} // key exists, value is None
{"found": false} // key doesn't exist
Most explicit. Self-documenting. No ambiguity. But changes the JSON shape for existing consumers.
Option B: Exit code + null
Return null for both absent cases under --json, but use exit code to distinguish (0 = found, 1 = not found). Scripts that don't care about the distinction just check for null. Scripts that do can check $?.
Option C: Empty stdout + exit code
Keep undefined for human-readable output. Under --json, emit nothing to stdout and set a non-zero exit code for "not found". This lets scripts do:
if RESERVES=$(dot ... --json 2>/dev/null); then
echo "$RESERVES" | jq ...
else
echo "not found"
fi
Most unix-y (how grep works). But non-zero exit codes can trip set -e scripts, and both "None" and "not found" would look the same to JSON consumers.
Key Files
src/core/output.ts—formatJson()andformatPretty()functions
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading src/core/output.ts, focusing on formatJson() and formatPretty(), then trace how --json handles missing storage values. Done means choosing and documenting one of the proposed semantics, preserving the distinction between absent keys and None values, and ensuring JSON-mode output is always valid JSON.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100