astrid-runtime/astrid · error · GatewayError::Internal
resolve workspace capsule manifest: {e}
Error message
resolve workspace capsule manifest: {e} What it means
After resolving the workspace, the handler resolves the capsule's Capsule.toml relative to the workspace and wraps resolution failure as an Internal error. This fails when the path cannot be canonicalized or safely joined (e.g. path traversal or invalid capsule id), before the existence check ever runs.
Source
Thrown at crates/astrid-gateway/src/routes/env.rs:385
.principal_home(principal)
.capsules_dir()
.join(capsule_id)
.join("Capsule.toml");
if principal_manifest.exists() {
return parse_env_schema(&principal_manifest);
}
let Some(workspace_root) = workspace_root else {
return Err(GatewayError::NotFound);
};
let workspace = workspace_layout
.resolve(workspace_root)
.map_err(|e| GatewayError::Internal(anyhow::anyhow!("resolve selected workspace: {e}")))?;
let manifest_relative = FsPath::new("capsules")
.join(capsule_id)
.join("Capsule.toml");
let workspace_manifest = workspace.resolve_file(&manifest_relative).map_err(|e| {
GatewayError::Internal(anyhow::anyhow!("resolve workspace capsule manifest: {e}"))
})?;
if !workspace_manifest.exists() {
return Err(GatewayError::NotFound);
}
let schema = parse_env_schema(&workspace_manifest)?;
workspace.resolve_file(&manifest_relative).map_err(|e| {
GatewayError::Internal(anyhow::anyhow!(
"workspace capsule manifest changed while it was being read: {e}"
))
})?;
Ok(schema)
}
#[cfg(test)]
fn parse_env_schema(manifest_path: &FsPath) -> GatewayResult<HashMap<String, EnvFieldSchema>> {
let text = match std::fs::read_to_string(manifest_path) {
Ok(t) => t,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {View on GitHub (pinned to affd8760f4)
Solutions
- Validate/sanitize the capsule id before calling the env-schema route (restrict to safe identifier characters)
- Confirm the capsule actually lives under capsules/<id>/Capsule.toml in the selected workspace
- Check daemon/gateway logs for the underlying resolve_file error to see whether it was traversal-blocked or IO-related
Example fix
// before
let manifest_relative = FsPath::new("capsules")
.join(capsule_id)
.join("Capsule.toml");
// after
if capsule_id.contains('/') || capsule_id.contains('\\') || capsule_id.contains("..") {
return Err(GatewayError::NotFound);
}
let manifest_relative = FsPath::new("capsules")
.join(capsule_id)
.join("Capsule.toml"); Defensive patterns
Strategy: validation
Validate before calling
fn is_safe_capsule_id(id: &str) -> bool {
!id.is_empty()
&& !id.contains('/')
&& !id.contains('\\')
&& !id.contains("..")
&& id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
} Type guard
fn safe_capsule_id(id: &str) -> Option<&str> {
(is_safe_capsule_id(id)).then_some(id)
} Try / catch
match load_env_schema_from_home(state, capsule_id).await {
Ok(schema) => schema,
Err(GatewayError::Internal(e)) if e.to_string().contains("resolve workspace capsule manifest") => {
tracing::warn!(%e, "capsule manifest path unresolvable");
GatewayError::NotFound
}
Err(e) => return Err(e),
} Prevention
- Sanitize capsule ids at the route layer before any filesystem resolution
- Keep Capsule.toml at the canonical capsules/<id>/Capsule.toml location
- Add tests for traversal-style capsule ids
When it happens
Trigger: Requesting the env schema for a capsule whose id makes capsules/<id>/Capsule.toml unresolvable inside the workspace — invalid characters, traversal attempts, or a workspace that rejects the file resolution.
Common situations: Capsule id containing path separators or unsafe characters from a client request; capsule id casing mismatch; workspace layout changes that invalidate resolve_file.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- resolve selected workspace: {e}
- workspace capsule manifest changed while it was being read:
- failed to resolve workspace root {}: {error}
- directory symlink {} not allowed in capsule source tree (ref
- workspace capsule directory is redirected: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/1c0bacbc3b9cc927.
Report an issue: GitHub.