fix(#1309): retry forge token read at startup instead of giving up immediately

This commit is contained in:
damocles 2026-06-04 21:15:03 +02:00
commit 84e468dadb

View file

@ -20,6 +20,13 @@ const POLL_INTERVAL_SECS: u64 = 30;
const HTTP_TIMEOUT_SECS: u64 = 10;
/// Maximum characters of a body/comment to include in the wake message.
const BODY_TRUNCATE: usize = 500;
/// How long to wait between token-read retries when the token file is
/// missing or unreadable at startup (e.g. hive-priv hasn't provisioned
/// it yet, or a chown race left it temporarily root-owned).
const TOKEN_RETRY_SECS: u64 = 30;
/// Give up waiting for the token after this many retries (~10 minutes).
/// Avoids an infinite wait on agents that genuinely have no forge account.
const TOKEN_RETRY_MAX: u32 = 20;
/// Spawn point: called once from `hive-ag3nt serve` (agent) or
/// `hive-m1nd serve` (manager). Returns immediately if the forge is not
@ -37,18 +44,33 @@ pub async fn run(socket: PathBuf) {
let state_dir = std::env::var("HYPERHIVE_STATE_DIR").unwrap_or_default();
let token_path = format!("{state_dir}/forge-token");
let token = match tokio::fs::read_to_string(&token_path).await {
Ok(t) => {
let t = t.trim().to_owned();
if t.is_empty() {
debug!("forge_notify: empty forge token at {token_path} — disabled");
// Retry reading the token to handle races where hive-priv provisions the
// token after the harness starts, or where a parent-container chown briefly
// makes the file unreadable (see #1304 / #1309). We wait up to
// TOKEN_RETRY_MAX * TOKEN_RETRY_SECS before giving up.
let token = {
let mut attempts = 0u32;
loop {
match tokio::fs::read_to_string(&token_path).await {
Ok(t) => {
let t = t.trim().to_owned();
if !t.is_empty() {
break t;
}
debug!("forge_notify: empty forge token at {token_path}");
}
Err(e) => {
debug!("forge_notify: cannot read token at {token_path}: {e}");
}
}
attempts += 1;
if attempts >= TOKEN_RETRY_MAX {
debug!(
"forge_notify: token not available after {TOKEN_RETRY_MAX} retries — disabled"
);
return;
}
t
}
Err(e) => {
debug!("forge_notify: no forge token at {token_path} ({e}) — disabled");
return;
tokio::time::sleep(Duration::from_secs(TOKEN_RETRY_SECS)).await;
}
};