Reason codes
Eighteen codes, frozen. Every one of them means a transaction confirmed on Bitcoin and the protocol declined to act on it. None of them means you lost coins.
- Where an invalid event surfaces, and what it names: the transaction, the height, the output when there is one, and a single code.
- All seven SEED codes, and what to change before you build a new commit.
- Which KEEP codes void one entry and which throw the whole marker away.
- The four codes that fire before any opcode rule runs.
- What a code you do not recognise tells you, and which two status fields to compare when you see one.
Where you see them
GET /patina/invalid-events
Each event names the transaction, the height, and one code. If your mint did not appear, this is where the explanation is.
SEED codes
One code per SEED check. The reveal confirmed whatever the protocol made of it, and the commit it spent cannot be spent twice, so a retry starts again from a new salt and a new commit.
| Code | What the parser saw | What it means for you |
|---|---|---|
SEED_BAD_GRAMMAR |
The marker did not decode as a SEED: wrong payload length, a non minimal push, more than one push, extra opcodes, or a flags byte that is not zero | A construction bug. Compare your script against the byte table on Marker grammar and try again with a new commit. |
SEED_NO_COMMIT_INPUT |
No input revealed a qualifying commit leaf | You spent the commit by the key path instead of the script path, or the leaf script did not match byte for byte. Retry with a new commit. |
SEED_COMMITMENT_MISMATCH |
A commit leaf was revealed, but
SHA256("PTNA/commit" || key || salt) did not equal the commitment in the leaf |
The salt in the marker is not the salt you committed to, or the key differs. Check both. Retry with a new commit. |
SEED_COMMIT_TOO_YOUNG |
Fewer than 144 blocks between the commit output's block and the reveal's block | You revealed early. The commit is spent and the claim is gone. Retry with a new commit and count the blocks. |
SEED_CARRIER_OUT_OF_RANGE |
carrier_vout names an output index that does not exist |
Off by one in the marker payload. Retry. |
SEED_CARRIER_IS_OPRETURN |
carrier_vout pointed at a data output |
You pointed the artifact at the marker itself. Retry. |
SEED_CARRIER_BELOW_MIN |
The carrier held less than 100000 sats for a founding claim, or less than 10000 sats otherwise | Underfunded carrier, often because fees came out of the wrong output. Retry with more in the carrier. |
KEEP codes
Two sizes of failure sit in this table. The four KEEP_ENTRY codes void one entry and leave
the rest of the marker working. KEEP_BAD_GRAMMAR and KEEP_DUPLICATE_INPUT are
decode failures that discard the whole marker, and KEEP_NO_CARRIER_INPUT means there was
nothing to route to begin with. The public risks page shows the same two
sizes side by side.
| Code | What the parser saw | What it means for you |
|---|---|---|
KEEP_BAD_GRAMMAR |
count was zero, above 8, or the payload length did not match count | The whole KEEP marker is ignored. Every carrier in the transaction followed the default rule. Check where each artifact landed. |
KEEP_NO_CARRIER_INPUT |
The transaction carried a KEEP marker but spent no carrier at all | Nothing happened. The marker was pointless, not harmful. |
KEEP_ENTRY_NOT_CARRIER |
An entry named an input that does not spend a carrier | That entry is void. Other valid entries still apply. |
KEEP_ENTRY_OUT_OF_RANGE |
An entry named a vout that does not exist | Entry void, default rule applies to that carrier. Check where the artifact went. |
KEEP_ENTRY_IS_OPRETURN |
An entry named a data output | Entry void, default rule applies. |
KEEP_ENTRY_BELOW_MIN |
An entry named an output holding less than 10000 sats | Entry void, default rule applies. This is the common one: the intended destination was too small. |
KEEP_DUPLICATE_INPUT |
Two entries named the same input index | An ambiguous instruction is a decode failure, so the whole marker is inert. Every carrier in the transaction followed the default rule. Check where each artifact landed. |
Marker codes
These four come from the layer above the opcodes. In all four the transaction is treated as carrying no usable marker, so a SEED creates nothing and a carrier spend falls to the default rule.
| Code | What the parser saw | What it means for you |
|---|---|---|
VOID_DUPLICATE_MARKER |
More than one output had a payload starting with PTNA |
The transaction is treated as unmarked. A SEED creates nothing. A carrier spend follows the default rule. |
MARKER_UNKNOWN_OP |
The opcode byte was not 0x01 or 0x02 |
Nothing happened. Either a construction bug, or a future opcode your indexer does not implement. |
MARKER_UNKNOWN_VERSION |
The version byte was not 0x01 |
Nothing happened. Same two possibilities. |
MARKER_TOO_LARGE |
The scriptPubKey exceeded 83 bytes, or the push payload exceeded 80 | Nothing happened. No legal marker is anywhere near that size, so this is a construction bug. |
Codes are frozen
This list of eighteen is part of the baseline. An implementation must not invent codes, rename them, or merge two into one. If you find a failure case that none of these describe, that is a specification gap and it should be reported, not papered over with a nineteenth code.
Two consequences for operators:
- Seeing a code you do not recognise means your software is older or newer than the one that produced the
event. Check
spec_sha256andparser_versionfromGET /patina/statuson both sides. - Codes are part of what implementations must agree on. Same chain, same codes, in the same order. See Golden vectors and state roots.
What none of these codes mean
- They never mean your bitcoin moved somewhere you did not send it.
- They never mean an artifact was taken from you.
- They never mean a transaction was rejected by Bitcoin. Every one of these transactions confirmed normally. The protocol simply declined to interpret it.