astrid-runtime/astrid · error · ContextualIoError
{context}
Error message
{context} What it means
with_context is the library's generic error-wrapper: it re-packages an underlying io::Error into a new io::Error of the same ErrorKind whose Display message is the supplied context string and whose source() chain preserves the original error. Hitting this means an OS operation failed and the library added human-readable context about which guarded file operation failed. Inspect the error's source() chain for the root cause (e.g. sharing violation, access denied, path not found).
Source
Thrown at crates/astrid-core/src/platform_fs/windows/error.rs:28
struct ContextualIoError {
context: String,
source: io::Error,
}
impl fmt::Display for ContextualIoError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}: {}", self.context, self.source)
}
}
impl std::error::Error for ContextualIoError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
pub(super) fn with_context(error: io::Error, context: impl Into<String>) -> io::Error {
io::Error::new(
error.kind(),
ContextualIoError {
context: context.into(),
source: error,
},
)
}
#[cfg(test)]
pub(super) fn native_error_code(error: &io::Error) -> Option<i32> {
if let Some(code) = error.raw_os_error() {
return Some(code);
}
let mut source = error.source();
while let Some(current) = source {
if let Some(error) = current.downcast_ref::<io::Error>()
&& let Some(code) = error.raw_os_error()
{View on GitHub (pinned to affd8760f4)
Solutions
- Inspect err.source() (walk the chain) to find the original io::Error kind and OS error code
- Retry the operation if the source kind was transient (sharing violation / lock temporarily held)
- Close other handles to the file (other processes, editors, AV scans) before retrying
- Run with sufficient privileges for operations inside the trusted directory
Example fix
// before: matching only on the wrapped message
if err.to_string().contains("replace") { ... }
// after: unwrap the context to branch on the root cause
let mut src: Option<&(dyn std::error::Error + 'static)> = err.source();
while let Some(e) = src { eprintln!("caused by: {e}"); src = e.source(); }
if err.kind() == io::ErrorKind::PermissionDenied { /* escalate or fix ACL */ } Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the guarded operation's target: existence and writability
fn can_modify(path: &std::path::Path) -> bool {
match std::fs::OpenOptions::new().write(true).open(path) {
Ok(_) => true,
Err(_) => false,
}
} Try / catch
match guarded_op() {
Err(e) if e.kind() == io::ErrorKind::PermissionDenied || e.kind() == io::ErrorKind::AlreadyExists => {
// walk e.source() for the native error code, then retry or escalate
}
other => other?,
} Prevention
- Always walk error.source() to see the native OS error behind the context wrapper
- Close competing handles (AV scanners, other processes) before file replace/move/delete
- Retry once on sharing violations before surfacing the error
- Run with privileges appropriate for the trusted directory
When it happens
Trigger: Any of the wrapped call sites fails: acquire_named_private_lock (lock contention/permissions), replace_file_checked, move_guarded_file, remove_guarded_file (rename/remove races or denied access) inside the guarded-file layer.
Common situations: File locked by another process or an antivirus scanner during replace/move; insufficient privileges to rename or delete inside a trusted directory; target path in use (ERROR_SHARING_VIOLATION); leftover handles from a crashed process holding the lock.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Windows named-pipe endpoint disappeared while waiting
- Windows named-pipe endpoint is absent
- Windows named-pipe endpoint is occupied but unavailable
- PermissionDenied
- InvalidData
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/b8dc5e5a9aa449ce.
Report an issue: GitHub.