openai/codex · error · anyhow::Error
failed to decrypt encrypted task id
Error message
failed to decrypt encrypted task id
What it means
Host::parse runs normalize_host (trim whitespace, strip brackets and a single :port, lowercase, drop trailing dots, canonicalize IP literals) and rejects a result that comes out empty. Host is the unit used for network policy evaluation, so callers are whatever feeds hosts into policy: config domain entries or host strings extracted from requests. Inputs that normalize to empty include an empty string, whitespace-only input, ":8080" (empty host before the port), and a lone "." (nothing left after trimming trailing dots).
Source
Thrown at codex-rs/agent-identity/src/lib.rs:421
}
let encrypted_task_id = response
.encrypted_task_id
.or(response.encrypted_task_id_camel)
.context("agent task registration response omitted task id")?;
decrypt_task_id_response(key, &encrypted_task_id)
}
pub fn decrypt_task_id_response(
key: AgentIdentityKey<'_>,
encrypted_task_id: &str,
) -> Result<String> {
let signing_key = signing_key_from_private_key_pkcs8_base64(key.private_key_pkcs8_base64)?;
let ciphertext = BASE64_STANDARD
.decode(encrypted_task_id)
.context("encrypted task id is not valid base64")?;
let plaintext = curve25519_secret_key_from_signing_key(&signing_key)
.unseal(&ciphertext)
.map_err(|_| anyhow::anyhow!("failed to decrypt encrypted task id"))?;
String::from_utf8(plaintext).context("decrypted task id is not valid UTF-8")
}
pub fn generate_agent_key_material() -> Result<GeneratedAgentKeyMaterial> {
let mut seed_material = [0u8; AGENT_IDENTITY_KEY_SEED_BYTES];
OsRng
.try_fill_bytes(&mut seed_material)
.context("failed to generate agent identity private key seed material")?;
// Ed25519 stores a 32-byte seed, so derive it from all sampled seed material.
let mut digest = Sha512::new();
digest.update(AGENT_IDENTITY_KEY_DERIVATION_CONTEXT);
digest.update(seed_material);
let digest = digest.finalize();
let mut secret_key_bytes = [0u8; 32];
secret_key_bytes.copy_from_slice(&digest[..32]);
let signing_key = SigningKey::from_bytes(&secret_key_bytes);
let private_key_pkcs8 = signing_key
.to_pkcs8_der()View on GitHub (pinned to 339751715c)
Solutions
- Reject or skip empty host strings before calling Host::parse
- When splitting host:port yourself, verify the host part is non-empty after the split
- If hosts come from URLs, parse with url::Url and require .host_str() to return Some and be non-empty
Example fix
// before
let host = Host::parse(raw)?; // raw may be "" or ":443" from a bad config entry
// after
if raw.trim().is_empty() || raw.trim().trim_end_matches('.').is_empty() {
continue; // skip malformed entry instead of failing policy build
}
let host = Host::parse(raw)?; Defensive patterns
Strategy: validation
Validate before calling
// Mirror of normalize_host for pre-checking callers
fn parseable_host(input: &str) -> bool {
let h = input.trim();
let h = h.strip_prefix('[').and_then(|r| r.split(']').next()).unwrap_or(h);
let h = if h.matches(':').count() == 1 { h.split(':').next().unwrap_or("") } else { h };
!h.to_ascii_lowercase().trim_end_matches('.').is_empty()
}
if !parseable_host(raw) {
return Err(anyhow!("unusable host string {raw:?}"));
}
let host = Host::parse(raw)?; Type guard
fn is_nonempty_host_str(input: &str) -> bool {
!input.trim().is_empty() && input.trim() != "." && !input.trim().starts_with(':')
} Try / catch
let host = match Host::parse(raw) {
Ok(host) => host,
Err(err) if err.to_string().contains("host is empty") => continue, // skip bad entry
Err(err) => return Err(err),
}; Prevention
- Filter empty strings out of host lists at config load
- When splitting host:port, validate the host half before use
- Derive hosts from url::Url::host_str() rather than string surgery
When it happens
Trigger: Calling Host::parse with an empty, whitespace-only, port-only (":443"), or dot-only (".") string — typically not hand-written but passed through from an empty host in a config list, or from URL handling where the authority was missing (e.g. a URL like http:///path with no host).
Common situations: Config lists containing empty strings from templating; upstream code splitting host:port and passing the empty half; request paths where a malformed URL yielded no host.
Related errors
- `bypass_hook_trust` override must be a boolean
- invalid value for `{field_name}`: `{candidate}` is not in th
- Unsupported platform: ${platform} (${arch})
- Unsupported target triple: ${targetTriple}
- Missing optional dependency ${platformPackage}. Reinstall Co
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/04945fd7f281581c.
Report an issue: GitHub.