juicedata/juicefs · error

Forbidden

Error message

Forbidden

What it means

The JuiceFS WebDAV handler returns HTTP 403 'Forbidden' when a GET request targets a directory while DisallowList is enabled. This prevents clients from browsing directory contents through WebDAV GET requests (which the handler would otherwise translate to PROPFIND). The check happens in ServeHTTP in pkg/fs/http.go before delegating to the underlying WebDAV handler.

Source

Thrown at pkg/fs/http.go:293

		}
		if userName != h.Username || pwd != h.Password {
			http.Error(w, "WebDAV: need authorized!", http.StatusUnauthorized)
			return
		}
	}

	// Excerpt from RFC4918, section 9.4:
	//
	// 		GET, when applied to a collection, may return the contents of an
	//		"index.html" resource, a human-readable view of the contents of
	//		the collection, or something else altogether.
	//
	// Get, when applied to collection, will return the same as PROPFIND method.
	if r.Method == "GET" && strings.HasPrefix(r.URL.Path, h.Handler.Prefix) {
		info, err := h.Handler.FileSystem.Stat(context.TODO(), strings.TrimPrefix(r.URL.Path, h.Handler.Prefix))
		if err == nil && info.IsDir() {
			if h.DisallowList {
				http.Error(w, "Forbidden", http.StatusForbidden)
				return
			}
			r.Method = "PROPFIND"
			if r.Header.Get("Depth") == "" {
				r.Header.Add("Depth", "1")
			}
		}
	}

	// The next line would normally be:
	//	http.Handle("/", h)
	// but we wrap that HTTP handler h to cater for a special case.
	//
	// The propfind_invalid2 litmus test case expects an empty namespace prefix
	// declaration to be an error. The FAQ in the webdav litmus test says:
	//
	// "What does the "propfind_invalid2" test check for?...
	//

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. If directory browsing is intended, restart the WebDAV server without the --disallow-list flag
  2. Access individual files directly instead of directory URLs (GET on a file is still allowed)
  3. Guard client code: issue PROPFIND/GET only on known file paths, or handle 403 as 'listing disabled' and skip directory traversal

Example fix

// before (client recursing directories)
for _, name := range listDir(url) { download(url + "/" + name) }
// after (handle 403 as listing disabled)
resp, _ := http.Get(dirURL)
if resp.StatusCode == http.StatusForbidden { log skip; return }
Defensive patterns

Strategy: fallback

Validate before calling

// WebDAV client: skip directories when listing is disallowed
func canGet(path string, isDir bool, disallowList bool) bool {
	return !isDir || !disallowList
}

Type guard

func isForbidden(resp *http.Response) bool { return resp.StatusCode == http.StatusForbidden }

Try / catch

resp, err := http.Get(dirURL)
if err != nil { return err }
if resp.StatusCode == http.StatusForbidden { /* listing disabled: skip */ return nil }

Prevention

When it happens

Trigger: Sending an HTTP GET to a path that resolves to a directory on the WebDAV server while the server was started with the --disallow-list option; the handler stats the path, sees info.IsDir()==true and h.DisallowList==true, and writes 'Forbidden' with status 403.

Common situations: Deployments that expose WebDAV only for direct file access (sharing individual file URLs) but disable directory listing for privacy or performance; users pasting a directory URL into a browser or running a recursive downloader against a disallow-list server.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/9a8e78513a55a21f. Report an issue: GitHub.