wezterm/wezterm · warning

failed to create symlink {} -> {}: {err:#}

Error message

failed to create symlink {} -> {}: {err:#}

What it means

Logged by AgentProxy (mux/src/ssh_agent.rs:72, update_symlink) when creating the runtime symlink agent.<PID> -> SSH_AUTH_SOCK fails with an error other than AlreadyExists. On Windows this is almost always the missing 'Create symbolic links' privilege; on Unix it is permission problems in the runtime directory or a read-only filesystem. The message embeds both paths plus the chained io error, and note the initial-symlink failure is only logged, not propagated: the AgentProxy keeps running without a working agent link.

Source

Thrown at mux/src/ssh_agent.rs:72

fn update_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> anyhow::Result<()> {
    let original = original.as_ref();
    let link = link.as_ref();

    match symlink_file(original, link) {
        Ok(()) => Ok(()),
        Err(err) => {
            if err.kind() == std::io::ErrorKind::AlreadyExists {
                std::fs::remove_file(link)
                    .with_context(|| format!("failed to remove {}", link.display()))?;
                symlink_file(original, link).with_context(|| {
                    format!(
                        "failed to create symlink {} -> {}: {err:#}",
                        link.display(),
                        original.display()
                    )
                })
            } else {
                anyhow::bail!(
                    "failed to create symlink {} -> {}: {err:#}",
                    link.display(),
                    original.display()
                );
            }
        }
    }
}

impl AgentProxy {
    pub fn new() -> Self {
        let pid = unsafe { libc::getpid() };
        let sock_path = config::RUNTIME_DIR.join(format!("agent.{pid}"));

        if let Some(inherited) = Self::default_ssh_auth_sock() {
            if let Err(err) = update_symlink(&inherited, &sock_path) {
                log::error!("failed to set {sock_path:?} to initial inherited SSH_AUTH_SOCK value of {inherited:?}: {err:#}");
            }

View on GitHub (pinned to 9c04f79f86)

Solutions

  1. On Windows: enable Developer Mode or grant the user/group the 'Create symbolic links' right (secpol.msc -> Local Policies -> User Rights Assignment)
  2. Fix permissions on the runtime dir (usually $XDG_RUNTIME_DIR/wezterm or the platform equivalent) so your user can write
  3. If a stale agent.<PID> file blocks the retry path, remove it and restart wezterm
  4. Set/repair SSH_AUTH_SOCK to a valid agent socket so the initial symlink target is sane

Example fix

# before: unprivileged Windows user
> wezterm.exe   # log: failed to create symlink ... agent.<PID>

# after: grant the right (admin shell)
> secpol.msc  # Local Policies -> User Rights Assignment ->
              # 'Create symbolic links' -> add your user, then sign out/in
Defensive patterns

Strategy: fallback

Validate before calling

// Probe writability of the runtime dir before relying on the agent symlink
use std::os::unix::fs::symlink;
fn runtime_dir_linkable() -> bool {
    let probe = config::RUNTIME_DIR.join(".link-probe");
    symlink("/nonexistent-target", &probe).is_ok() || {
        let ok = probe.exists();
        let _ = std::fs::remove_file(&probe);
        ok || probe.symlink_metadata().is_ok()
    }
}

Try / catch

// Note: the initial-symlink failure is already only logged by AgentProxy::new();
// downstream, treat a missing agent.<PID> link as degraded (no agent forwarding)
// rather than fatal:
if !sock_path.symlink_metadata().is_ok() {
    log::warn!("ssh agent proxy unavailable; SSH_AUTH_SOCK passthrough degraded");
}

Prevention

When it happens

Trigger: Running wezterm on Windows as a user without the SeCreateSymbolicLinkPrivilege; RUNTIME_DIR not writable (root-owned, full disk, sandboxed home); security software blocking symlink creation; the target SSH_AUTH_SOCK path being on a filesystem that rejects links.

Common situations: Fresh Windows box where Developer Mode / the symlink privilege was never enabled; XDG_RUNTIME_DIR pointing at a directory with wrong ownership after a UID change; containers with read-only /run.

Related errors


AI-assisted analysis of wezterm/wezterm@9c04f79f86 (2026-08-16). Data as JSON: /api/errors/e95c7c8f840aeaee. Report an issue: GitHub.