slimtoolkit/slim · error

root %q is not absolute

Error message

root %q is not absolute

What it means

The fastcgi RoundTrip transport requires the FastCGI application's document root (t.Root) to be an absolute filesystem path because the FastCGI protocol sends the ROOT document path to the backend. Before dialing the backend, buildEnv strips IPv6 brackets from the host and validates that Root starts with '/'; if it does not, the request cannot be converted into a valid FastCGI PARAMS block and this error is returned to the caller of RoundTrip.

Source

Thrown at pkg/app/master/probe/http/internal/fastcgi.go:141

// buildEnv returns a set of CGI environment variables for the request.
func (t FastCGITransport) buildEnv(r *http.Request) (map[string]string, error) {

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.LastIndex(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Remove [] from IPv6 addresses
	ip = strings.Replace(ip, "[", "", 1)
	ip = strings.Replace(ip, "]", "", 1)

	// make sure file root is absolute
	if !path.IsAbs(t.Root) {
		return nil, fmt.Errorf("root %q is not absolute", t.Root)
	}
	root := t.Root

	fpath := r.URL.Path
	scriptName := fpath

	docURI := fpath
	// split "actual path" from "path info" if configured
	var pathInfo string
	if splitPos := t.splitPos(fpath); splitPos > -1 {
		docURI = fpath[:splitPos]
		pathInfo = fpath[splitPos:]

		// Strip PATH_INFO from SCRIPT_NAME
		scriptName = strings.TrimSuffix(scriptName, pathInfo)
	}

	// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME

View on GitHub (pinned to 81940d17fa)

Solutions

  1. Set t.Root to an absolute filesystem path (must start with '/'), e.g. "/srv/www/current/public".
  2. If the root comes from config/env, convert it with filepath.Abs before constructing the transport.
  3. Verify the value is non-empty; an empty Root is not absolute and fails the same check.
  4. On macOS/Windows-style inputs, ensure separators and drive prefixes are converted to the container/OS absolute form before use.

Example fix

// before
transport := &fastcgi.Transport{Root: "public", ...}
// after
root, err := filepath.Abs("public")
if err != nil {
    return err
}
transport := &fastcgi.Transport{Root: root, ...}
Defensive patterns

Strategy: validation

Validate before calling

if !filepath.IsAbs(root) || root == "" {
    abs, err := filepath.Abs(root)
    if err != nil {
        return fmt.Errorf("cannot resolve fastcgi root %q: %w", root, err)
    }
    root = abs
}
transport := &fastcgi.Transport{Root: root, ...}

Type guard

func isAbsoluteRoot(root string) bool {
    return root != "" && filepath.IsAbs(root)
}

Try / catch

resp, err := client.Do(req)
if err != nil {
    if strings.Contains(err.Error(), "is not absolute") {
        return nil, fmt.Errorf("fix fastcgi transport Root to an absolute path: %w", err)
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling http.Client.Do (or Transport.RoundTrip) on a client whose Transport is the fastcgi handler transport with a target whose Root field was set to a relative path (e.g. "www", "./public", or an empty string) instead of an absolute path like "/var/www".

Common situations: Configuring the FastCGI target root from a config flag or environment variable that holds a relative directory; forgetting to prefix the root with '/' when hand-building the fastcgi handler; passing a URL-style path ("/app" vs actual filesystem path) or leaving Root unset (empty string is not absolute).

Related errors


AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31). Data as JSON: /api/errors/1fad225d7d705aba. Report an issue: GitHub.