AlistGo/alist · error
cannot remove a submodule
Error message
cannot remove a submodule
What it means
During Remove the driver fetches the parent git tree and looks for the entry matching the object's name. A tree entry with Type == "commit" is a git submodule pointer, not a regular blob or tree. The driver cannot compose a valid delete commit for a submodule link, so it explicitly rejects the operation with this error.
Source
Thrown at drivers/github/driver.go:572
return d.commit(message, rootSha)
}
func (d *Github) Remove(ctx context.Context, obj model.Obj) error {
if !d.isOnBranch {
return errors.New("cannot write to non-branch reference")
}
d.commitMutex.Lock()
defer d.commitMutex.Unlock()
parentDir := stdpath.Dir(obj.GetPath())
tree, treeSha, err := d.getTreeDirectly(parentDir)
if err != nil {
return err
}
var del *TreeObjReq = nil
for _, t := range tree.Trees {
if t.Path == obj.GetName() {
if t.Type == "commit" {
return errors.New("cannot remove a submodule")
}
del = &t.TreeObjReq
del.Sha = nil
break
}
}
if del == nil {
return errs.ObjectNotFound
}
newTree := make([]interface{}, 0, 2)
newTree = append(newTree, *del)
if len(tree.Trees) == 1 { // completely emptying the repository will get a 404
newTree = append(newTree, map[string]string{
"path": ".gitkeep",
"mode": "100644",
"type": "blob",
"content": "",
})View on GitHub (pinned to 843d9dc814)
Solutions
- Remove the submodule with plain git: git rm <path>, delete the .gitmodules entry, commit and push
- Edit .gitmodules and the gitlink entry manually via a commit, then let OpenList re-list
- If submodules are not wanted, replace them with vendored copies or a package manager
Example fix
# before: delete submodule via OpenList UI -> error # after: in a local clone git rm --cached <submodule-path> git rm <submodule-path> git commit -m "remove submodule" git push
Defensive patterns
Strategy: validation
Validate before calling
// Check tree entry type before calling Remove
tree, _, _ := d.getTreeDirectly(stdpath.Dir(obj.GetPath()))
for _, t := range tree.Trees {
if t.Path == obj.GetName() && t.Type == "commit" {
return errors.New("path is a submodule; remove it with git")
}
}
return d.Remove(ctx, obj) Type guard
func isSubmoduleEntry(t TreeEntry) bool { return t.Type == "commit" } Try / catch
if err := d.Remove(ctx, obj); err != nil {
if strings.Contains(err.Error(), "cannot remove a submodule") {
log.Warnf("%s is a submodule; removing via git instead", obj.GetPath())
return nil // handled out-of-band
}
return err
} Prevention
- List submodules (git submodule status) and treat them as opaque in UI flows
- Filter Type==commit entries out of user-deletable listings
- Automate submodule removal in CI with git rm, not through the file API
When it happens
Trigger: Calling Remove on a directory or file whose corresponding entry in the GitHub tree has type 'commit' — i.e. the path is a submodule defined in .gitmodules; the loop in Remove finds t.Type == "commit" and bails out before building the new tree.
Common situations: A repo vendors dependencies as submodules (common in C/C++ projects, dotfiles frameworks, themes) and the user tries to delete the submodule folder through the OpenList file manager.
Related errors
- cannot copy a submodule
- cannot download a submodule
- cannot move a submodule
- cannot rename a submodule
- chunk part has no readable link
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/f6cf72bc9810eadc.
Report an issue: GitHub.