IceWhaleTech/CasaOS · error
mount failed
Error message
mount failed
What it means
Returned by Mount() in pkg/utils/httper/drive.go when the POST to the rclone REST endpoint /mount/mount completes but responds with a non-200 status. The CasaOS 'drive' layer is a thin HTTP wrapper over rclone's librclone/rest API, so a non-200 means rclone itself refused the mount (bad fs name, invalid options, or busy mount point). The error carries no detail because the code formats a static string and discards res.Body(), where rclone puts the actual reason.
Source
Thrown at pkg/utils/httper/drive.go:91
json.Unmarshal(res.Body(), &result)
for i := 0; i < len(result.MountPoints); i++ {
result.MountPoints[i].Fs = result.MountPoints[i].Fs[:len(result.MountPoints[i].Fs)-1]
}
return result, err
}
func Mount(mountPoint string, fs string) error {
res, err := NewRestyClient().R().SetFormData(map[string]string{
"mountPoint": mountPoint,
"fs": fs,
"mountOpt": `{"AllowOther": true}`,
"vfsOpt": `{"CacheMode": 3}`,
}).Post("/mount/mount")
if err != nil {
return err
}
if res.StatusCode() != 200 {
return fmt.Errorf("mount failed")
}
logger.Info("mount then", zap.Any("res", res.Body()))
return nil
}
func Unmount(mountPoint string) error {
res, err := NewRestyClient().R().SetFormData(map[string]string{
"mountPoint": mountPoint,
}).Post("/mount/unmount")
if err != nil {
logger.Error("when unmount", zap.Error(err))
return err
}
if res.StatusCode() != 200 {
logger.Error("then unmount failed", zap.Any("res", res.Body()))
return fmt.Errorf("unmount failed")
}
logger.Info("unmount then", zap.Any("res", res.Body()))View on GitHub (pinned to 0d3b2f444e)
Solutions
- Check the rclone/drive service is running and healthy (systemctl status or the CasaOS service list) before calling Mount.
- Verify the fs config exists first with GetConfigByName(name) and that its type/backend parameters are valid.
- Confirm the mountPoint directory exists, is empty, and is not already mounted (mountpoint -q /mnt point).
- Ensure /etc/fuse.conf contains user_allow_other, since mountOpt sends AllowOther:true.
- Improve the error to include res.StatusCode() and string(res.Body()) so rclone's actual refusal reason is visible.
Example fix
// before
if res.StatusCode() != 200 {
return fmt.Errorf("mount failed")
}
// after
if res.StatusCode() != 200 {
return fmt.Errorf("mount failed: status=%d body=%s", res.StatusCode(), res.Body())
} Defensive patterns
Strategy: validation
Validate before calling
// before calling Mount()
if _, err := httper.GetConfigByName(fs); err != nil {
return fmt.Errorf("fs %q not configured: %w", fs, err)
}
if err := unix.Access(mountPoint, unix.W_OK); err != nil {
return fmt.Errorf("mount point %q not writable: %w", mountPoint, err)
}
// reject an already-mounted point
if mnts, _ := os.ReadFile("/proc/mounts"); strings.Contains(string(mnts), " "+mountPoint+" ") {
return fmt.Errorf("%s already mounted", mountPoint)
} Try / catch
if err := httper.Mount(point, fs); err != nil {
if strings.Contains(err.Error(), "mount failed") {
// rclone refused: surface body detail, do not blind-retry (option errors are deterministic)
logger.Error("rclone refused mount", zap.String("fs", fs), zap.Error(err))
return err
}
// transport error: retry once after confirming the drive service is up
return retryOnce(func() error { return httper.Mount(point, fs) })
} Prevention
- Create and validate the rclone config before ever calling Mount.
- Keep mountPoint paths canonical (filepath.Clean) and store them so mount/unmount use identical strings.
- Enable user_allow_other in /etc/fuse.conf at image/provisioning time, since mountOpt always sends AllowOther:true.
- Wrap drive helpers so status and response body are always included in returned errors.
When it happens
Trigger: POST /mount/mount with form fields mountPoint, fs, mountOpt={"AllowOther":true}, vfsOpt={"CacheMode":3} against the rclone service returns non-200: (1) fs names a config that does not exist in rclone, (2) mountPoint path does not exist or is already mounted, (3) AllowOther requires user_allow_other in /etc/fuse.conf and it is disabled, (4) the rclone REST service is being restarted concurrently.
Common situations: Drive feature of CasaOS after a factory reset where rclone configs were wiped; upgrading rclone changed option names so previously valid mountOpt/vfsOpt JSON is rejected; running in a container without /dev/fuse or with FUSE not installed; leftover stale mount from a crashed process blocking the same mountPoint.
Related errors
AI-assisted analysis of IceWhaleTech/CasaOS@0d3b2f444e (2026-08-15).
Data as JSON: /api/errors/b8a749dd4521af57.
Report an issue: GitHub.