{"record":{"id":"b3e1487020c0961c","repo":"AlistGo/alist","slug":"cannot-rename-a-submodule","errorCode":null,"errorMessage":"cannot rename a submodule","messagePattern":"cannot rename a submodule","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"drivers/github/driver.go","lineNumber":487,"sourceCode":"}\n\nfunc (d *Github) Rename(ctx context.Context, srcObj model.Obj, newName string) error {\n\tif !d.isOnBranch {\n\t\treturn errors.New(\"cannot write to non-branch reference\")\n\t}\n\td.commitMutex.Lock()\n\tdefer d.commitMutex.Unlock()\n\tparentDir := stdpath.Dir(srcObj.GetPath())\n\ttree, _, err := d.getTreeDirectly(parentDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewTree := make([]interface{}, 2)\n\toperated := false\n\tfor _, t := range tree.Trees {\n\t\tif t.Path == srcObj.GetName() {\n\t\t\tif t.Type == \"commit\" {\n\t\t\t\treturn errors.New(\"cannot rename a submodule\")\n\t\t\t}\n\t\t\tdelCopy := t.TreeObjReq\n\t\t\tdelCopy.Sha = nil\n\t\t\tnewTree[0] = delCopy\n\t\t\tt.Path = newName\n\t\t\tnewTree[1] = t.TreeObjReq\n\t\t\toperated = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !operated {\n\t\treturn errs.ObjectNotFound\n\t}\n\tnewSha, err := d.newTree(tree.Sha, newTree)\n\tif err != nil {\n\t\treturn err\n\t}\n\trootSha, err := d.renewParentTrees(parentDir, tree.Sha, newSha, \"/\")","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/github/driver.go#L469-L505","documentation":"Rename() iterates the parent tree looking for the entry to rename; when found, its Type is 'commit' — a submodule pointer. Submodules live in .gitmodules and point into another repository, so the tree API cannot rename them, and the driver rejects it explicitly. drivers/github/driver.go:487.","triggerScenarios":"Attempting to rename a submodule entry (tree entry of type 'commit') in a mounted GitHub repository.","commonSituations":"Submodule directories appearing like normal folders in the file browser and being renamed via the UI.","solutions":["Rename the submodule with git (update .gitmodules and the tree entry) and push.","Treat submodule entries as read-only in the mounted view.","Hide submodule entries to prevent accidental operations."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"for _, t := range tree.Trees {\n    if t.Path == srcObj.GetName() && t.Type == \"commit\" {\n        return errors.New(\"submodule entries cannot be renamed via the tree API\")\n    }\n}","typeGuard":"func isSubmoduleTreeEntry(t TreeObj) bool { return t.Type == \"commit\" }","tryCatchPattern":"if err := d.Rename(ctx, obj, name); err != nil && strings.Contains(err.Error(), \"submodule\") {\n    // guide to .gitmodules + git mv\n}","preventionTips":["Do not offer rename on type=='commit' entries.","Manage submodule names through .gitmodules.","Surface entry type metadata in the UI to set expectations."],"tags":["github","submodule","rename","tree-api","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}