zed-industries/zed · error

failed to change ownership for zed_remote_server via chown:

Error message

failed to change ownership for zed_remote_server via chown: {}

What it means

After docker cp succeeds, Zed runs `docker exec <container> chown <remote_user>:<remote_user> <dst_path>` so the uploaded remote server is owned by the connecting user. This error means that chown exited non-zero and its stderr is included verbatim.

Source

Thrown at crates/remote/src/transport/docker.rs:477

        let mut chown_command = util::command::new_command(&docker_cli);
        chown_command.kill_on_drop(true);
        chown_command.arg("exec");
        chown_command.arg(connection_options.container_id);
        chown_command.arg("chown");
        chown_command.arg(format!(
            "{}:{}",
            connection_options.remote_user, connection_options.remote_user,
        ));
        chown_command.arg(&dst_path);

        let output = chown_command.output().await?;

        if output.status.success() {
            return Ok(());
        }

        let stderr = String::from_utf8_lossy(&output.stderr);
        log::debug!("failed to change ownership for via chown: {stderr}",);
        anyhow::bail!(
            "failed to change ownership for zed_remote_server via chown: {}",
            stderr,
        );
    }

    async fn upload_file(
        &self,
        src_path: &Path,
        dest_path: &RelPath,
        remote_dir_for_server: &str,
    ) -> Result<()> {
        log::debug!("uploading file {:?} to {:?}", src_path, dest_path);

        let src_path_display = src_path.display().to_string();
        let dest_path_str = dest_path.display(self.path_style());
        let full_server_path = format!("{}/{}", remote_dir_for_server, dest_path_str);

View on GitHub (pinned to 5a9b9558db)

Solutions

  1. Verify the user exists: `docker exec <id> id <remote_user>`; if missing, add it (alpine: `adduser -D <user>`) or connect as a user present in the image
  2. Run the failing command manually to see the exact stderr: `docker exec <id> chown <user>:<user> <dst_path>`
  3. Connect as root, or use an image where the connecting user can chown its own files
  4. For rootless/userns docker, confirm the uid mapping covers the remote user's uid

Example fix

# before
FROM alpine
COPY app /app

# after
FROM alpine
RUN adduser -D devuser
COPY --chown=devuser:devuser app /app
USER devuser
Defensive patterns

Strategy: try-catch

Validate before calling

async fn user_exists_in_container(conn: &DockerExecConnection, user: &str) -> bool {
    conn.run_docker_exec("id", None, &Default::default(), &[user])
        .await
        .is_ok()
}

Try / catch

if let Err(e) = result {
    if e.to_string().contains("chown") {
        // surface stderr and suggest adding remote_user to the image or connecting as root
    }
    return Err(e);
}

Prevention

When it happens

Trigger: chown fails inside the container: remote_user has no entry in the container's /etc/passwd, docker exec runs as a user lacking CAP_CHOWN, the destination path is on a read-only filesystem, or user namespace remapping shifts the uid/gid.

Common situations: Connecting with a username that was never added to the image; minimal images (alpine without shadow, distroless) with no user provisioning; rootless docker or userns-remap installs where ids map differently; images mounted read-only.

Related errors


AI-assisted analysis of zed-industries/zed@5a9b9558db (2026-08-20). Data as JSON: /api/errors/abe854c914918f52. Report an issue: GitHub.