OP_VERIF and OP_VERNOTIF - Always-Invalid Opcodes
18. OP_VERIF and OP_VERNOTIF — Always-Invalid Opcodes
Overview
OP_VERIF (byte 0x65) and OP_VERNOTIF (byte 0x66) are two opcodes that share a unique and extreme status in Bitcoin Script: they are always invalid regardless of context. Unlike disabled opcodes (which fail during execution) or OP_RESERVED (which fails if executed), OP_VERIF and OP_VERNOTIF cause immediate script invalidity even if they appear inside an OP_IF/OP_ENDIF block that is never entered.
Historical Origin
These opcodes were originally intended to complement OP_VER. The envisioned semantics were:
OP_VERIF: Execute the next block if the protocol version matches
OP_VERNOTIF: Execute the next block if the protocol version does NOT match
They would have functioned like OP_IF and OP_NOTIF, but conditioned on the protocol version rather than a stack item. This design was abandoned along with OP_VER for similar reasons: version-conditional script logic creates unresolvable consensus problems as the protocol evolves.
Always-Invalid Behavior
The distinguishing characteristic of OP_VERIF and OP_VERNOTIF is that they fail even in unexecuted branches. This is sometimes described as "super-disabled":
Case 1: In main execution path
Script: OP_1 OP_VERIF OP_2
Result: ALWAYS INVALID
Case 2: In false branch of OP_IF
Script: OP_0 OP_IF OP_VERIF OP_ENDIF
Result: ALWAYS INVALID (even though branch is never entered)
Case 3: In nested false branch
Script: OP_0 OP_IF OP_0 OP_IF OP_VERIF OP_ENDIF OP_ENDIF
Result: ALWAYS INVALID
This is in contrast to OP_RESERVED:
Script: OP_0 OP_IF OP_RESERVED OP_ENDIF
Result: VALID (OP_RESERVED in dead branch is fine)
Script: OP_0 OP_IF OP_VERIF OP_ENDIF
Result: INVALID (OP_VERIF anywhere is always invalid)
Implementation in Bitcoin Core
// From src/script/interpreter.cpp
// OP_VERIF and OP_VERNOTIF are explicitly invalid
// They appear in the 'always invalid regardless of fExec' check
if (opcode == OP_VERIF || opcode == OP_VERNOTIF)
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
This check runs unconditionally, before the fExec (execution flag) is consulted. Even if the interpreter is in a "skip" state (inside a false conditional branch), encountering OP_VERIF or OP_VERNOTIF causes immediate failure.
Why This Property Matters
The "always invalid even in dead branches" property has an important implication: any script that contains OP_VERIF or OP_VERNOTIF can never be valid, and therefore any output locked with such a script is permanently unspendable. This is stronger than OP_RETURN (discussed in the next section) in the sense that the invalidity cannot be worked around by clever script construction.
It also means that these opcodes cannot be used as upgrade vehicles via soft forks (unlike NOP opcodes), since any scripts using them are already irreversibly invalid.
TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation: