{"record":{"id":"3ac1ed37cbb828a9","repo":"astrid-runtime/astrid","slug":"opaque-capsule-assets-cannot-be-symlinks","errorCode":null,"errorMessage":"opaque capsule assets cannot be symlinks: {}","messagePattern":"opaque capsule assets cannot be symlinks: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-build/src/archiver.rs","lineNumber":50,"sourceCode":"        if metadata.file_type().is_symlink() {\n            anyhow::bail!(\n                \"opaque capsule asset directories cannot be symlinks: {}\",\n                root.display()\n            );\n        }\n        if !metadata.is_dir() {\n            anyhow::bail!(\"opaque asset path must be a directory: {}\", root.display());\n        }\n        let mut pending = vec![root];\n        while let Some(dir) = pending.pop() {\n            for entry in fs::read_dir(&dir)\n                .with_context(|| format!(\"Failed to read asset directory: {}\", dir.display()))?\n            {\n                let entry = entry?;\n                let path = entry.path();\n                let file_type = entry.file_type()?;\n                if file_type.is_symlink() {\n                    anyhow::bail!(\n                        \"opaque capsule assets cannot be symlinks: {}\",\n                        path.display()\n                    );\n                }\n                if file_type.is_dir() {\n                    pending.push(path);\n                } else if file_type.is_file() {\n                    files.push(path);\n                }\n            }\n        }\n    }\n    files.sort();\n    Ok(files)\n}\n\n/// Packages a set of files and directories into a single `.capsule` (tar.gz) archive.\npub(crate) fn pack_capsule_archive(","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-build/src/archiver.rs#L32-L68","documentation":"In the FUSE storage provider's hidden service mode, run_launch computes the parent-challenge for the readiness handshake via storage_provider_service_ready_challenge and maps any error it returns into an anyhow error using the error's Display text ('{error}'). It indicates the launch handshake inputs (parent token, mount id, paths) failed challenge derivation, so the service cannot prove itself to the parent process.","triggerScenarios":"run (FUSE service mode) receives a StorageProviderServiceLaunchV1 on stdin, passes validate_launch, then storage_provider_service_ready_challenge returns Err — e.g. malformed or empty parent token, or invalid path/UUID inputs — and run_launch propagates it verbatim.","commonSituations":"Parent process passed an empty or wrongly-encoded parent.token; control/resource/callback paths contain invalid characters or are empty; mount_id UUID serialization mismatch between parent and provider versions.","solutions":["Inspect the inner error text to see which challenge input failed, then fix the parent-side launch payload accordingly.","Ensure launch.parent.token is a valid, non-empty token produced by storage_provider_service_ready_challenge's counterpart on the parent side.","Verify control_path, resource_path and callback_path are absolute, valid paths and mount_id is a well-formed UUID."],"exampleFix":"// before\nlet challenge = storage_provider_service_ready_challenge(\n    &launch.parent.token, /* token from hand-built launch JSON */ ...\n).map_err(|error| anyhow::anyhow!(error))?;\n// after\n// construct the launch via the parent SDK so the token is derived from the same inputs\nlet launch = StorageProviderServiceLaunchV1::build(&lease, &parent)?;\nlet challenge = storage_provider_service_ready_challenge(\n    &launch.parent.token, ...\n).map_err(|error| anyhow::anyhow!(error))?;","handlingStrategy":"try-catch","validationCode":"fn launch_inputs_ok(launch: &StorageProviderServiceLaunchV1) -> bool {\n    !launch.parent.token.is_empty()\n        && launch.lease.mount_id.as_uuid() != uuid::Uuid::nil()\n        && !launch.control_path.as_os_str().is_empty()\n}","typeGuard":null,"tryCatchPattern":"match run_launch(launch).await {\n    Err(e) => { eprintln!(\"FUSE launch failed: {e:#}\"); std::process::exit(1); },\n    Ok(()) => {},\n}","preventionTips":["Construct launch payloads via the parent-side SDK helper","Log the full error chain ({e:#}) to find the failing input","Keep parent and provider versions in sync"],"tags":["fuse","ipc","handshake"],"backgroundTag":"invalid-config-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}