Skip to content

[Detail Bug] list-access-tokens panics when expires_at is NoneΒ #131

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/bugs/bug_4280a84a-09ab-476c-b4ea-717dd7c08ab4

Summary

  • Context: The list_tokens function in src/main.rs displays access tokens returned by the list_access_tokens API call.
  • Bug: The function calls .expect("expires_at") on an Option<u32> field that can be None, causing a panic.
  • Actual vs. expected: When listing access tokens without an expiration date, the CLI panics instead of gracefully handling the missing value.
  • Impact: Users cannot list access tokens if any token in their account lacks an expiration date, causing the entire CLI command to crash.

Code with bug

async fn list_tokens(
    client_config: ClientConfig,
    prefix: Option<String>,
    start_after: Option<String>,
    limit: Option<usize>,
    no_auto_paginate: bool,
) -> Result<(), S2CliError> {
    let account_service = AccountService::new(Client::new(client_config));
    let tokens = account_service.list_access_tokens(
        prefix.unwrap_or_default(),
        start_after.unwrap_or_default(),
        limit,
        no_auto_paginate,
    );

    tokio::pin!(tokens);

    while let Some(token) = tokens.next().await {
        for token_info in token?.access_tokens {
            let exp_date = token_info
                .expires_at
                .map(|exp| {
                    humantime::format_rfc3339_seconds(
                        UNIX_EPOCH + Duration::from_secs(exp as u64),
                    )
                    .to_string()
                    .green()
                })
                .expect("expires_at");  // <-- BUG πŸ”΄ Panics when expires_at is None

            println!(
                "{} {}",
                token_info.id.parse::<String>().expect("id"),
                exp_date
            );
        }
    }

    Ok(())
}

Example

  1. Create an access token without an expiration date:
    s2 issue-access-token --id my-token --ops read
    # No --expires-at flag
  2. List tokens:
    s2 list-access-tokens
  3. The code path processes expires_at = None and panics:
    token_info
        .expires_at       // None
        .map(|exp| ...)   // -> None
        .expect("expires_at"); // panics on None
  4. Resulting error:
    called 'Option::unwrap()' on a 'None' value: expires_at
    

Recommended fix

let exp_date = token_info
    .expires_at
    .map(|exp| {
        humantime::format_rfc3339_seconds(
            UNIX_EPOCH + Duration::from_secs(exp as u64),
        )
        .to_string()
        .green()
    })
    .unwrap_or_else(|| "never".to_string().cyan());  // <-- FIX 🟒 Handle None case

println!(
    "{} {}",
    token_info.id.parse::<String>().expect("id"),
    exp_date
);

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions