Skip to content

Error Handling

Handle verification failures gracefully with clear error messages and proper validation.

This is part of the Verification Strategy for World ID Bridge integration.

Why Error Handling Matters

World ID verification can fail for various reasons: invalid proofs, network issues, or replay attempts. Without proper error handling, users get confusing failures and your application becomes unreliable.

Good error handling provides clear feedback for users, debugging information for developers, graceful recovery instead of crashes, and security protection against invalid inputs.

Basic Implementation

Define Clear Error Messages

Create organized error codes that are easy to understand:

mod Errors {
    pub const PROOF_VERIFICATION_FAILED: felt252 = 'Proof verification failed';
    pub const INVALID_PROOF_FORMAT: felt252 = 'Invalid proof format';
    pub const EMPTY_PROOF_DATA: felt252 = 'Proof data is empty';
    pub const NULLIFIER_ALREADY_USED: felt252 = 'Nullifier already used';
}

Validate Inputs Early

Check for common issues before attempting verification:

fn _validate_proof_input(proof: Span<felt252>) -> bool {
    assert(proof.len() > 0, Errors::EMPTY_PROOF_DATA);
    assert(proof.len() >= 8, Errors::INVALID_PROOF_FORMAT);
    true
}

Handle Verification Results

Use pattern matching to handle different outcomes:

fn _verify_human(ref self: ContractState, proof: Span<felt252>) -> bool {
    // Validate input first
    self._validate_proof_input(proof);
    
    let world_id = IGroth16VerifierBN254Dispatcher { 
        contract_address: self.world_id_bridge.read() 
    };
 
    match world_id.verify_groth16_proof_bn254(proof) {
        Option::Some(public_inputs) => {
            assert(public_inputs.len() >= 4, Errors::INVALID_PROOF_FORMAT);
            
            let nullifier = *public_inputs.at(1);
            assert(nullifier != 0, 'Invalid nullifier');
            assert(!self.used_nullifiers.entry(nullifier).read(), Errors::NULLIFIER_ALREADY_USED);
            
            self.used_nullifiers.entry(nullifier).write(true);
            true
        },
        Option::None => {
            assert(false, Errors::PROOF_VERIFICATION_FAILED);
        }
    }
}

Simple Example

Here's a basic World ID contract with error handling:

#[starknet::interface]
pub trait IMyWorldIDApp<TContractState> {
    fn claim_reward(ref self: TContractState, proof: Span<felt252>) -> bool;
}
 
mod Errors {
    pub const PROOF_VERIFICATION_FAILED: felt252 = 'Proof verification failed';
    pub const INVALID_PROOF_FORMAT: felt252 = 'Invalid proof format';
    pub const EMPTY_PROOF_DATA: felt252 = 'Proof data is empty';
    pub const NULLIFIER_ALREADY_USED: felt252 = 'Nullifier already used';
}
 
#[starknet::contract]
pub mod MyWorldIDApp {
    use starknet::ContractAddress;
    use starknet::storage::*;
    use super::{Errors, IMyWorldIDApp};
 
    #[storage]
    pub struct Storage {
        world_id_bridge: ContractAddress,
        used_nullifiers: Map<u256, bool>,
    }
 
    #[constructor]
    fn constructor(ref self: ContractState, world_id_bridge_address: ContractAddress) {
        self.world_id_bridge.write(world_id_bridge_address);
    }
 
    #[generate_trait]
    impl InternalFunctions of InternalFunctionsTrait {
        fn _validate_proof_input(self: @ContractState, proof: Span<felt252>) -> bool {
            assert(proof.len() > 0, Errors::EMPTY_PROOF_DATA);
            assert(proof.len() >= 8, Errors::INVALID_PROOF_FORMAT);
            true
        }
 
        fn _verify_human(ref self: ContractState, proof: Span<felt252>) -> bool {
            self._validate_proof_input(proof);
            
            // Add your verification logic here
            // This is where you'd call the World ID Bridge
            
            true // Simplified for example
        }
    }
 
    #[abi(embed_v0)]
    impl MyWorldIDAppImpl of IMyWorldIDApp<ContractState> {
        fn claim_reward(ref self: ContractState, proof: Span<felt252>) -> bool {
            if self._verify_human(proof) {
                // Execute your application logic
                true
            } else {
                false
            }
        }
    }
}

Key Points

Validate inputs before expensive operations, use descriptive error messages with assert, check array bounds before accessing elements, and handle both success and failure cases in your verification logic.

For more advanced verification patterns, see Nullifier Tracking.