rustfs/rustfs · warning · HeartbeatError
RustFS is not registered with Connect
Error message
RustFS is not registered with Connect
What it means
Every heartbeat builds its mTLS client from a stored device credential; when credential_store.load() returns None (device.crt.json absent), the node has no Connect identity, so TelemetryError::NotRegistered maps to HeartbeatError::NotRegistered. It means registration never completed for this state directory — a pending registration file alone is not enough, the device credential only exists after completion. Heartbeats cannot proceed until registration finishes.
Source
Thrown at rustfs/src/connect/heartbeat.rs:369
}
fn fsync_dir(directory: &Path) -> io::Result<()> {
#[cfg(unix)]
fs::File::open(directory)?.sync_all()?;
#[cfg(not(unix))]
let _ = directory;
Ok(())
}
#[derive(Debug, thiserror::Error)]
pub enum HeartbeatError {
#[error("Connect heartbeat endpoint must be an HTTPS base URL without credentials, query, or fragment")]
Endpoint,
#[error("Connect heartbeat root CA configuration is invalid")]
RootCertificate,
#[error("Connect heartbeat schedule is invalid")]
Schedule,
#[error("RustFS is not registered with Connect")]
NotRegistered,
#[error("the Connect device private key is missing")]
IdentityMissing,
#[error("the stored Connect certificate and device private key cannot form a TLS identity")]
IdentityCertificate,
#[error("the stored Connect credential name is invalid")]
CredentialName,
#[error("the stored Connect device certificate is not currently valid")]
CredentialExpired,
#[error("the Connect heartbeat node summary is outside protocol bounds")]
NodeSummary,
#[error("the Connect heartbeat sequence is exhausted")]
SequenceExhausted,
#[error("a Connect heartbeat runtime already owns this state")]
AlreadyRunning,
#[error("the persisted Connect heartbeat changed while delivery was in flight")]
StateConflict,
#[error("Connect heartbeat state I/O failed at {path}: {source}")]View on GitHub (pinned to 201c653dcd)
Solutions
- Complete the Connect registration flow (token -> registration) so device.crt.json is persisted in the credential directory
- Verify the credential directory is the same one registration wrote to and that device.crt.json exists with mode 0600
- If registration was completed before, restore the state directory from backup; otherwise run registration again
Defensive patterns
Strategy: try-catch
Validate before calling
let credential_file = Path::new(&connect_state_dir).join("device.crt.json");
if !credential_file.exists() {
// run Connect registration before starting the heartbeat loop
} Type guard
fn is_not_registered(err: &HeartbeatError) -> bool {
matches!(err, HeartbeatError::NotRegistered)
} Try / catch
match sender.send(&pending).await {
Err(HeartbeatError::NotRegistered) => {
// expected pre-registration: defer/suspend heartbeat, surface registration prompt;
// do NOT tight-loop retries — registration is an operator action
}
result => result,
} Prevention
- Gate heartbeat startup on a completed registration check
- Keep the connect state directory on a durable, backed-up volume
- Monitor for the credential file's absence and alert before it matters
When it happens
Trigger: Starting the heartbeat/telemetry loop on a fresh install before running Connect registration; registration started but interrupted before device.crt.json was written; the credential directory deleted, moved, or pointing at the wrong path (load() only treats NotFound as 'no credential').
Common situations: New deployments skipping onboarding; restoring cluster data without the connect state directory; containers losing the state volume on restart; env var typos pointing at an empty directory.
Related errors
- state path has no parent
- state filename is invalid
- Connect heartbeat schedule is invalid
- the stored Connect device certificate is not currently valid
- the Connect heartbeat node summary is outside protocol bound
AI-assisted analysis of rustfs/rustfs@201c653dcd (2026-08-23).
Data as JSON: /api/errors/605edc3980209dd1.
Report an issue: GitHub.