Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Error Types

InvocationError

All Client async methods return Result<T, InvocationError>:

#![allow(unused)]
fn main() {
pub enum InvocationError {
    Rpc(RpcError),        // Telegram returned an error response
    Deserialize(String),  // failed to decode the server's binary response
    Io(std::io::Error),   // network or IO failure
}
}

RpcError

#![allow(unused)]
fn main() {
pub struct RpcError {
    pub code:    i32,
    pub message: String,
}
}

Error code groups

CodeCategoryMeaning
303See OtherDC migration — handled automatically by layer
400Bad RequestWrong parameters, invalid data
401UnauthorizedNot logged in, session invalid/expired
403ForbiddenInsufficient permissions
404Not FoundResource doesn’t exist
406Not AcceptableContent not acceptable
420FloodFLOOD_WAIT_X — rate limited
500Server ErrorTelegram internal error, retry later

Common error messages

MessageCauseFix
PHONE_NUMBER_INVALIDBad phone formatUse E.164 format: +12345678900
PHONE_CODE_INVALIDWrong codeAsk user to try again
PHONE_CODE_EXPIREDCode timed outCall request_login_code again
SESSION_PASSWORD_NEEDED2FA requiredUse check_password
PASSWORD_HASH_INVALIDWrong 2FA passwordRe-prompt the user
PEER_ID_INVALIDUnknown peerResolve peer first or check the ID
ACCESS_TOKEN_INVALIDBad bot tokenCheck token from @BotFather
CHAT_WRITE_FORBIDDENCan’t post hereBot not in group or read-only channel
USER_PRIVACY_RESTRICTEDPrivacy blocks actionCan’t message/add this user
FLOOD_WAIT_NRate limitedWait N seconds (AutoSleep handles this)
FILE_PARTS_INVALIDUpload errorRetry the upload
MEDIA_EMPTYNo media providedCheck your InputMedia
MESSAGE_NOT_MODIFIEDEdit with no changesEnsure new text differs
BOT_INLINE_DISABLEDInline mode offEnable in @BotFather
QUERY_ID_INVALIDCallback too oldAnswer within 60 seconds

SignInError

Returned specifically by client.sign_in():

#![allow(unused)]
fn main() {
pub enum SignInError {
    PasswordRequired(PasswordToken), // 2FA is on — pass to check_password()
    InvalidCode,                     // wrong code submitted
    Other(InvocationError),          // anything else
}
}

Full error handling example

#![allow(unused)]
fn main() {
use layer_client::{InvocationError, RpcError, SignInError};
use std::time::Duration;

// Login errors
match client.sign_in(&token, &code).await {
    Ok(name)                                       => println!("✅ {name}"),
    Err(SignInError::PasswordRequired(pw))         => handle_2fa(pw).await?,
    Err(SignInError::InvalidCode)                  => println!("❌ Wrong code"),
    Err(SignInError::Other(InvocationError::Rpc(e))) => println!("RPC {}: {}", e.code, e.message),
    Err(SignInError::Other(e))                     => println!("IO/decode error: {e}"),
}

// General method errors
match client.send_message("@user", "hi").await {
    Ok(_) => {}

    // Rate limit (only visible if using NoRetries policy)
    Err(InvocationError::Rpc(RpcError { code: 420, ref message, .. })) => {
        let secs: u64 = message
            .strip_prefix("FLOOD_WAIT_")
            .and_then(|s| s.parse().ok())
            .unwrap_or(60);
        println!("Rate limited. Sleeping {secs}s");
        tokio::time::sleep(Duration::from_secs(secs)).await;
    }

    // Permission error
    Err(InvocationError::Rpc(RpcError { code: 403, ref message, .. })) => {
        println!("Permission denied: {message}");
    }

    // Network error
    Err(InvocationError::Io(e)) => {
        println!("Network error: {e}");
        // Consider reconnecting
    }

    Err(e) => eprintln!("Unexpected: {e}"),
}
}

Implementing From for your error type

#![allow(unused)]
fn main() {
#[derive(Debug)]
enum MyError {
    Telegram(layer_client::InvocationError),
    Io(std::io::Error),
    Custom(String),
}

impl From<layer_client::InvocationError> for MyError {
    fn from(e: layer_client::InvocationError) -> Self {
        MyError::Telegram(e)
    }
}

// Now you can use ? throughout your handlers
async fn my_handler(client: &Client) -> Result<(), MyError> {
    client.send_message("me", "hello").await?;  // auto-converts
    Ok(())
}
}