{"record":{"id":"c20c13720b6f758a","repo":"AlistGo/alist","slug":"cannot-download-a-submodule","errorCode":null,"errorMessage":"cannot download a submodule","messagePattern":"cannot download a submodule","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"drivers/github/driver.go","lineNumber":174,"sourceCode":"\t\treturn ret, nil\n\t} else {\n\t\tret := make([]model.Obj, 0, len(obj.Entries))\n\t\tfor _, entry := range obj.Entries {\n\t\t\tif entry.Name != \".gitkeep\" {\n\t\t\t\tret = append(ret, entry.toModelObj())\n\t\t\t}\n\t\t}\n\t\treturn ret, nil\n\t}\n}\n\nfunc (d *Github) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {\n\tobj, err := d.get(file.GetPath())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif obj.Type == \"submodule\" {\n\t\treturn nil, errors.New(\"cannot download a submodule\")\n\t}\n\turl := obj.DownloadURL\n\tghProxy := strings.TrimSpace(d.Addition.GitHubProxy)\n\tif ghProxy != \"\" {\n\t\turl = strings.Replace(url, \"https://raw.githubusercontent.com\", ghProxy, 1)\n\t}\n\treturn &model.Link{\n\t\tURL: url,\n\t}, nil\n}\n\nfunc (d *Github) MakeDir(ctx context.Context, parentDir model.Obj, dirName 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\tparent, err := d.get(parentDir.GetPath())","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/github/driver.go#L156-L192","documentation":"Link() refuses to produce a download link when the fetched object's type is 'submodule': a git submodule entry points at a commit SHA in another repository, and the GitHub contents/raw API cannot serve it as a file. The driver surfaces this as an explicit, user-facing restriction instead of returning a broken URL.","triggerScenarios":"Clicking/downloading a directory entry whose tree type is 'submodule' (a .gitmodules entry) in a mounted GitHub repo. drivers/github/driver.go:174.","commonSituations":"Repos vendoring dependencies as submodules (common in C/C++ projects); users browsing a repo through OpenList and trying to download the submodule folder like a normal directory.","solutions":["Do not download submodule entries through this mount; clone the repo with 'git clone --recurse-submodules' instead.","If submodules are noise, ignore them in listings or hide them with the driver/front-end filter options.","There is no driver-side fix — the raw API genuinely has no blob for a submodule pointer."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"obj, err := d.get(file.GetPath())\nif err != nil { return nil, err }\nif obj.Type == \"submodule\" {\n    return nil, errors.New(\"cannot download a submodule\")\n}","typeGuard":"func isSubmodule(obj *TreeObj) bool {\n    return obj != nil && obj.Type == \"submodule\"\n}","tryCatchPattern":"link, err := d.Link(ctx, file, args)\nif err != nil && err.Error() == \"cannot download a submodule\" {\n    // skip in UI / guide user to git clone --recurse-submodules\n}","preventionTips":["Mark submodule entries as non-downloadable in listings.","Use git clone --recurse-submodules for submodule content.","Educate users that submodules are pointers, not blobs."],"tags":["github","submodule","download","link","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}