Skip to content

prepare_canonical: unhelpful error when chgrp fails on files not owned by the user #1

@ligon

Description

@ligon

Problem

When the canonical repository contains files not owned by the current
user (e.g. files created by the agent user or by root),
prepare_canonical fails silently or with a generic CommandError
because chgrp -R cannot change the group on those files.

The consequence for sucoder collaborate (which calls bootstrap
prepare_canonicalensure_clone) is:

  1. prepare_canonical runs chgrp -R coder <canonical> — this
    partially fails (exit code 1) on files not owned by the user.
  2. Because check=True, CommandError is raised with a generic
    "Command failed with exit code 1: chgrp -R coder …" message.
    The chgrp stderr (e.g. chgrp: changing group of '…': Operation not permitted) is logged but not surfaced clearly.
  3. The MirrorError handler in the CLI prints the error and exits,
    but the user is left without a clear diagnosis or remediation path.

In practice this presents as an opaque failure (or, in some
configurations, a loop where prepare_canonical keeps retrying).

Expected behavior

When chgrp/chmod fails due to file ownership, prepare_canonical
should:

  1. Detect the specific "Operation not permitted" failure.
  2. List the offending files (or at least the first few).
  3. Suggest a concrete fix, e.g.:
Cannot change group on 3 files not owned by you:
  src/data/cache.pkl  (owner: coder)
  .git/objects/pack/pack-abc123.idx  (owner: root)
  .git/objects/pack/pack-abc123.pack  (owner: root)

Fix with:
  sudo chown ligon:ligon <files…>
  # then re-run sucoder collaborate

Where to fix

  • mirror.py:prepare_canonical (line ~203–205): catch CommandError
    from the chgrp -R call, parse stderr for "Operation not permitted"
    lines, extract the failing paths, and raise a MirrorError with
    actionable guidance.

Reproducer

# Create a file owned by root inside the canonical repo
sudo touch /path/to/canonical/root-owned-file

# Now try to collaborate
sucoder collaborate
# → opaque "Command failed" error

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions