{"record":{"id":"9e013edc6530ecf0","repo":"AlistGo/alist","slug":"cannot-write-to-non-branch-reference","errorCode":null,"errorMessage":"cannot write to non-branch reference","messagePattern":"cannot write to non-branch reference","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/github/driver.go","lineNumber":188,"sourceCode":"\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())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif parent.Entries == nil {\n\t\treturn errs.NotFolder\n\t}\n\tsubDirSha, err := d.newTree(\"\", []interface{}{\n\t\tmap[string]string{\n\t\t\t\"path\":    \".gitkeep\",\n\t\t\t\"mode\":    \"100644\",\n\t\t\t\"type\":    \"blob\",\n\t\t\t\"content\": \"\",\n\t\t},\n\t})","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/github/driver.go#L170-L206","documentation":"MakeDir() (and every write operation) checks d.isOnBranch first: writes are only possible when the configured ref resolved to an actual branch. If the storage was configured with a tag name, a commit SHA, or a non-branch ref, the GitHub API cannot create commits on it, so the driver rejects the operation up front. drivers/github/driver.go:188.","triggerScenarios":"Calling MakeDir on a storage whose ref field is a tag (e.g. v1.0.0), a 40-hex commit SHA, or 'HEAD' detached — anything where getRepo()/ref resolution did not land on a branch.","commonSituations":"User pins the mount to a release tag for stability and then tries to create a folder through the UI; ref set to a commit SHA for reproducibility; fork mounted at a non-default ref that is actually a tag.","solutions":["Change the storage's ref configuration to a real branch name (e.g. main or master) and re-save.","Leave ref empty so the driver uses the repo's default branch.","If a read-only mount on a tag is intended, treat this error as expected and avoid write operations."],"exampleFix":"// before (storage config)\nRef: \"v1.2.3\"\n\n// after\nRef: \"main\"","handlingStrategy":"validation","validationCode":"// after Init resolves the ref\nif !d.isOnBranch {\n    return errors.New(\"github storage ref is not a branch; write operations disabled\")\n}","typeGuard":"func (d *Github) canWrite() bool { return d.isOnBranch }","tryCatchPattern":"if err := d.MakeDir(ctx, parent, name); err != nil && err.Error() == \"cannot write to non-branch reference\" {\n    // surface 'read-only mount (tag/SHA ref)' to the user\n}","preventionTips":["Configure the ref as a branch name or leave it empty for the default branch.","Expose read-only mode in the UI when the ref is a tag/SHA.","Guard automation to check isOnBranch before any write."],"tags":["github","write","branch","ref","read-only","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}