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
- Treat the error as expected: skip all telemetry work for this run and do not log it as a failure
- 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
- Verify the root path / CODEWHALE_HOME you passed actually belongs to the profile you intend
- 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
- Treat 'telemetry is disabled' as control flow, not a failure; never log it as an error
- Re-enable telemetry only via the re-consent (arm) flow — never delete the tombstone by hand
- Check the consent decision before computing telemetry work so opted-out homes never reach the call
- Point CODEWHALE_HOME at the intended profile before assuming a bug
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
- parallel(): expected an array of thunks
- unknown argument: ${arg}
- --days must be an integer from 1 through 90
- Cloudflare SQL request failed (${response.status}): ${body}
- CF_ACCOUNT_ID and CF_API_TOKEN are required
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/144097dc68bba781.
Report an issue: GitHub.