gleam-lang/gleam · error

OAuth credentials TOML encoding

Error message

OAuth credentials TOML encoding

What it means

Panic from `toml::to_string(&credentials).expect("OAuth credentials TOML encoding")` in encrypt_and_store_oauth_refresh_token in compiler-cli/src/hex/auth.rs. Serializing the Hex credentials struct to TOML failed — practically only possible if the struct contains types the toml serializer cannot represent (e.g. None-bearing fields where the schema expects strings, or map types TOML can't express). The stored OAuth credentials file therefore cannot be written.

Solutions

  1. Inspect the credentials struct and ensure all fields are TOML-compatible (strings, plain structs, no nested maps under non-string keys, no unexpected None).
  2. Replace the expect with proper error propagation so the user sees a message instead of a panic.
  3. Update the serde attributes (skip_serializing_if = "Option::is_none") on fields that may be absent.
  4. Pin or upgrade the toml crate deliberately after verifying the struct still serializes; add a round-trip unit test.

Example fix

// before
let toml = toml::to_string(&credentials).expect("OAuth credentials TOML encoding");
// after
let toml = toml::to_string(&credentials)
    .map_err(|e| anyhow::anyhow!("Failed to encode OAuth credentials as TOML: {e}"))?;
Defensive patterns

Strategy: validation

Validate before calling

// Round-trip check before storing:
if toml::to_string(&credentials).is_err() { eprintln!("credentials not TOML-serializable"); }

Type guard

fn toml_serializable<T: serde::Serialize>(v: &T) -> bool { toml::to_string(v).is_ok() }

Try / catch

match toml::to_string(&credentials) { Ok(t) => crate::fs::write(&path, &t)?, Err(e) => return Err(anyhow::anyhow!("TOML encoding failed: {e}")) }

Prevention

When it happens

Trigger: Completing the OAuth device flow (create_and_store_new_credentials_via_oauth) or refreshing stored tokens (read_and_decrypt_and_refresh_stored_tokens) when the credentials struct shape can't be represented in TOML — e.g. after a struct field type change in HexAuthentication's credentials model.

Common situations: A refactor changes a credentials field to a nested map/Option layout the toml crate rejects; upgrading the toml crate introduces stricter serializer rules.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@15b07c7830 (2026-09-14). Data as JSON: /api/errors/439ef1968b6d67c2. Report an issue: GitHub.

Appendix: source

Thrown at compiler-cli/src/hex/auth.rs:139

        let encrypted_refresh_token =
            encryption::encrypt_with_passphrase(tokens.refresh_token.as_bytes(), &local_password)
                .map_err(|error| Error::FailedToEncryptLocalHexApiKey {
                detail: error.to_string(),
            })?;

        let credentials = StoredOAuthCredentials {
            hexpm: StoredOAuthRepoCredentials {
                api: self.hex_config.api_base.clone(),
                repository: self.hex_config.repository_base.clone(),
                refresh_token: encrypted_refresh_token,
                refresh_token_hash: {
                    let mut hasher = sha2::Sha256::new();
                    hasher.update(tokens.refresh_token.as_bytes());
                    base16::encode_lower(&hasher.finalize())
                },
            },
        };
        let toml = toml::to_string(&credentials).expect("OAuth credentials TOML encoding");
        crate::fs::write(&path, &toml)?;
        Ok(())
    }

    /// Create a new local password.
    ///
    /// The password must be long enough.
    ///
    /// The old password will be discarded, and the new one will be both
    /// returned and stored in `self.local_password`
    ///
    fn ask_for_new_local_password(&mut self) -> Result<()> {
        let required_length = 8;
        self.local_password = None;
        println!(
            "Please enter a new unique password, at least {required_length} characters long.
It will be used to locally encrypt your Hex API tokens.
"

View on GitHub (pinned to 15b07c7830)