astrid-runtime/astrid · error
principal ' ' has no admitted durable UID
Error message
principal '{principal}' has no admitted durable UID What it means
resolve_principal_uid looks up the authenticated principal in the daemon's admin AgentList roster and extracts its owner_uid (the durable immutable UID used to build the log directory path). If the principal is not in the roster, or its entry has no owner_uid (not yet admitted), the lookup fails with this error.
Solutions
- Ensure the agent/principal is registered and admitted (`aos agents list` to confirm it appears with an owner_uid)
- Re-admit the agent if it is pending, then retry the logs command
- Verify the principal id spelling matches the roster entry exactly
Example fix
// guard before calling
let entries = list_agents().await?;
anyhow::ensure!(
entries.iter().any(|e| e.principal == *principal && e.owner_uid.is_some()),
"principal not admitted yet"
); Defensive patterns
Strategy: try-catch
Validate before calling
// confirm the principal is admitted with a UID before reading capsule logs
let entries = agent_list().await?;
let admitted = entries.iter().any(|e| e.principal == *principal && e.owner_uid.is_some());
if !admitted { eprintln!("principal '{principal}' not admitted yet"); } Type guard
fn admitted_with_uid(e: &AgentEntry) -> Option<PrincipalUid> { e.owner_uid } Try / catch
match resolve_principal_uid(principal).await {
Ok(uid) => uid,
Err(e) if e.to_string().contains("has no admitted durable UID") => {
eprintln!("Admit the agent first (aos agents list/admit), then retry");
return Err(e);
}
} Prevention
- Complete agent admission before querying per-capsule logs
- Verify principal ids against the roster instead of typing from memory
- Handle daemon restarts where the roster may be briefly empty
When it happens
Trigger: `astrid logs` for a capsule calls resolve_log_dir -> resolve_principal_uid when: the principal was never admitted to the daemon, the agent hasn't been registered/admitted yet, or the roster entry lacks an owner_uid assignment.
Common situations: Querying logs for a brand-new agent before admission completes; typo'd principal id; daemon restarted and roster not yet reloaded; revoked agent whose UID was cleared.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Admin request timed out after
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon is recorded as running (PID file) but its…
- anyhow::anyhow!(error)
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7b20cb0fe273296a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/logs.rs:70
if newest.as_ref().is_none_or(|(t, _)| *t < modified) {
newest = Some((modified, path));
}
}
Ok(newest.map(|(_, p)| p))
}
async fn resolve_principal_uid(principal: &PrincipalId) -> Result<PrincipalUid> {
let mut client = crate::admin_client::connect_as_active_agent().await?;
let body = client.request(AdminRequestKind::AgentList).await?;
let body = crate::admin_client::into_result(body)?;
let AdminResponseBody::AgentList(entries) = body else {
anyhow::bail!("unexpected response while resolving principal UID: {body:?}");
};
entries
.into_iter()
.find(|entry| entry.principal == *principal)
.and_then(|entry| entry.owner_uid)
.ok_or_else(|| anyhow::anyhow!("principal '{principal}' has no admitted durable UID"))
}
async fn resolve_log_dir(principal: &PrincipalId, capsule: Option<&str>) -> Result<PathBuf> {
let home = AstridHome::resolve().context("Failed to resolve Astrid home directory")?;
Ok(match capsule {
// Capsule runtime logs are operational state, not home content. The
// daemon's immutable-UID projection owns the canonical path; resolve
// the UID through the authenticated admin roster before reading it.
Some(name) => home
.log_dir()
.join("principals")
.join(resolve_principal_uid(principal).await?.to_string())
.join(name),
None => home.log_dir(),
})
}
/// Print the last `n` lines of `path` to stdout. For non-huge logs weView on GitHub (pinned to affd8760f4)