{"record":{"id":"24540f3600afc8f6","repo":"Pumpkin-MC/Pumpkin","slug":"i-o-error-with-license-storage-0","errorCode":null,"errorMessage":"I/O error with license storage: {0}","messagePattern":"I/O error with license storage: (.+?)","errorType":"exception","errorClass":"LicenseError","httpStatus":null,"severity":"error","filePath":"crates/pumpkin-plugin-utils/src/license.rs","lineNumber":30,"sourceCode":"use tracing::{debug, info};\n\n/// License checking and verification errors.\n#[derive(Debug, Error)]\npub enum LicenseError {\n    /// HTTP communication error with marketplace.\n    #[error(\"Marketplace HTTP error: {0}\")]\n    Http(#[from] HttpError),\n    /// Metadata validation error (e.g. missing license on paid plugin).\n    #[error(\"License metadata mismatch: {0}\")]\n    MetadataMismatch(String),\n    /// License revoked or refunded by marketplace.\n    #[error(\"License was revoked or refunded: {0}\")]\n    Revoked(String),\n    /// License is expired.\n    #[error(\"License has expired on {0}\")]\n    Expired(String),\n    /// I/O error reading/writing license cache.\n    #[error(\"I/O error with license storage: {0}\")]\n    Io(#[from] std::io::Error),\n    /// JSON serialization error.\n    #[error(\"JSON serialization error: {0}\")]\n    Json(#[from] serde_json::Error),\n    /// Plugin is unsigned or missing marketplace metadata.\n    #[error(\"Plugin is unsigned or missing marketplace metadata\")]\n    UnsignedPlugin,\n    /// Plugin has not been initialized.\n    #[error(\n        \"Plugin-utils has not been initialized (call pumpkin_plugin_utils::init(context) first)\"\n    )]\n    NotInitialized,\n}\n\n/// Manages license checks, cached leases, and offline grace periods.\npub struct LicenseChecker {\n    data_folder: PathBuf,\n    http_client: HttpClient,","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin-plugin-utils/src/license.rs#L12-L48","documentation":"LicenseError::Io wraps std::io::Error (#[from]) from reading or writing the license cache on disk. The library persists license leases for offline grace; a filesystem failure while loading or saving that cache is surfaced here. The underlying OS error is embedded via {0}.","triggerScenarios":"Loading the cached lease at startup or writing a refreshed lease after verification when file open/read/write fails: missing parent directory, insufficient permissions, disk full, or the cache path points to something unusable (e.g. a directory).","commonSituations":"Read-only filesystem or container with no writable data dir; cache directory deleted between runs; running the server under a user lacking permissions on the cache path; disk quota/full disk; cache file corrupted at the OS level.","solutions":["Check the license cache path exists and is writable by the server process (permissions/ownership).","Ensure the parent directory exists (the library may not create it) or create it manually.","Free disk space / raise quota if the write failed due to space.","Run the server as a user with access to the data directory; fix read-only mounts.","Inspect {0} for the specific OS error (Permission denied, No space left, Is a directory)."],"exampleFix":"// before\nstd::fs::create_dir_all(\"plugins/.licenses\"); // was missing\ndrop(manager.verify());\n// after\nlet cache_dir = Path::new(\"plugins/.licenses\");\nstd::fs::create_dir_all(&cache_dir)?;\n// verify cache is writable\nlet probe = cache_dir.join(\".probe\");\nstd::fs::write(&probe, b\"\")?;\nstd::fs::remove_file(&probe)?;","handlingStrategy":"try-catch","validationCode":"// Verify cache directory is writable before license operations\nlet dir = Path::new(\"plugins/.licenses\");\nstd::fs::create_dir_all(dir)?;\nlet probe = dir.join(\".write_probe\");\nstd::fs::write(&probe, b\"\")?;\nstd::fs::remove_file(&probe)?;","typeGuard":"fn is_io_error(e: &LicenseError) -> bool {\n    matches!(e, LicenseError::Io(_))\n}","tryCatchPattern":"match manager.verify() {\n    Err(LicenseError::Io(e)) => {\n        tracing::warn!(\"license cache I/O failed: {e}; running without cache\");\n        verify_without_cache()\n    }\n    other => other.map(|_| ()),\n}","preventionTips":["Provision a writable data directory for the server process.","Run the server under a user with ownership of the data dir.","Monitor disk space/quota on the host.","Never mount the data directory read-only."],"tags":["io","filesystem","license-cache","rust"],"backgroundTag":"file-read-failed","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}