Hmbown/CodeWhale · info

telemetry is disabled

Error message

telemetry is disabled

What it means

Thrown by read_or_create_install_id when the telemetry opt-out tombstone (the `disabled` marker file under <root>/telemetry) is present. The tombstone is the durable record of a telemetry opt-out; every envelope surface refuses to read or mint data while it exists. This is expected control flow, not a malfunction.

Source

Thrown at crates/telemetry/src/envelope.rs:64

    /// When a flush was last *attempted*, RFC3339 UTC. Attempt, not success, so
    /// a permanently offline machine tries at most once per interval.
    #[serde(default)]
    pub last_flush: Option<String>,
}

/// Read the install id, minting a fresh one if it is missing, unreadable,
/// **not a UUID**, or older than [`ROTATION_DAYS`].
///
/// The UUID check is not a formatting nicety. `install_id` is the one
/// envelope field read verbatim off disk into a batch, so without it the file
/// is a free-form string slot on the wire for anything that can write
/// `$CODEWHALE_HOME/telemetry/install_id.json`. Minting a fresh random id is
/// always the safe direction — the cost is one rotation, and the docs already
/// say no count derived from `install_id` is a user count.
pub fn read_or_create_install_id(root: &Path) -> Result<InstallId> {
    buffer::try_with_lock(root, || {
        if buffer::tombstone_present(root) {
            anyhow::bail!("telemetry is disabled");
        }
        let path = buffer::install_id_path(root);
        let existing = std::fs::read_to_string(&path)
            .ok()
            .and_then(|body| serde_json::from_str::<InstallId>(&body).ok())
            .filter(|record| uuid::Uuid::parse_str(record.install_id.trim()).is_ok())
            .filter(|record| !is_expired(&record.rotated_at));
        if let Some(record) = existing {
            return Ok(record);
        }
        let record = InstallId {
            schema_version: 1,
            install_id: uuid::Uuid::new_v4().to_string(),
            rotated_at: now_rfc3339(),
        };
        codewhale_config::persistence::atomic_write_json(&path, &record)
            .with_context(|| format!("failed to write {}", path.display()))?;
        Ok(record)

View on GitHub (pinned to 8880682c63)

Solutions

  1. Treat the error as expected: skip all telemetry work for this run and do not log it as a failure
  2. If telemetry should be enabled, re-consent through the supported arm() flow (buffer::arm clears the tombstone for the current generation); never delete the tombstone by hand
  3. Verify the root path / CODEWHALE_HOME you passed actually belongs to the profile you intend
  4. Gate calls behind a tombstone/consent check so opted-out homes never reach this code path

Example fix

// before
let id = envelope::read_or_create_install_id(&root)?;
// after
if buffer::tombstone_present(&root) {
    return Ok(None); // opted out: telemetry intentionally skipped
}
let id = envelope::read_or_create_install_id(&root)?;
Defensive patterns

Strategy: try-catch

Try / catch

match envelope::read_or_create_install_id(&root) {
    Ok(id) => { /* proceed */ }
    Err(err) if err.to_string() == "telemetry is disabled" => { /* opted out: skip telemetry silently */ }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Calling envelope::read_or_create_install_id(root) on a home where telemetry was wiped/opted out (buffer::wipe wrote the tombstone first and never removes it there), or any CODEWHALE_HOME whose telemetry directory contains the tombstone file. The check runs inside try_with_lock before install_id.json is ever read.

Common situations: A machine where the user disabled telemetry via settings or the wipe flow; CI or shared environments that pre-create the tombstone; unit tests pointing read_or_create_install_id at a tombstoned fixture home; a stray CODEWHALE_HOME env var pointing at an opted-out profile.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/144097dc68bba781. Report an issue: GitHub.