tailscale/tailscale · error · AccessDeniedError

errorMessageFromBody(all)

Error message

errorMessageFromBody(all)

What it means

Thrown by checkPartitionFits in `tailscale configure flash-appliance`: a GAF member ("boot.img" or "root.img") has UncompressedSize64 larger than the fixed gokrazy partition it must be written into (disklayout.BootPartitionSizeMB or RootPartitionSizeMB in MB). The CLI refuses to flash because the image would overflow its partition in the GPT layout.

Source

Thrown at client/local/local.go:172

		}
	})
	if !lc.OmitAuth {
		if _, token, err := safesocket.LocalTCPPortAndToken(); err == nil {
			req.SetBasicAuth("", token)
		}
	}
	return lc.tsClient.Do(req)
}

func (lc *Client) doLocalRequestNiceError(req *http.Request) (*http.Response, error) {
	res, err := lc.DoLocalRequest(req)
	if err == nil {
		if server := res.Header.Get("Tailscale-Version"); server != "" && server != envknob.IPCVersion() && onVersionMismatch != nil {
			onVersionMismatch(envknob.IPCVersion(), server)
		}
		if res.StatusCode == 403 {
			all, _ := io.ReadAll(res.Body)
			return nil, &AccessDeniedError{errors.New(errorMessageFromBody(all))}
		}
		if res.StatusCode == http.StatusPreconditionFailed {
			all, _ := io.ReadAll(res.Body)
			return nil, &PreconditionsFailedError{errors.New(errorMessageFromBody(all))}
		}
		return res, nil
	}
	if ue, ok := err.(*url.Error); ok {
		if oe, ok := ue.Err.(*net.OpError); ok && oe.Op == "dial" {
			path := req.URL.Path
			pathPrefix, _, _ := strings.Cut(path, "?")
			return nil, fmt.Errorf("Failed to connect to local Tailscale daemon for %s; %s Error: %w", pathPrefix, tailscaledConnectHint(), oe)
		}
	}
	return nil, err
}

type errorJSON struct {

View on GitHub (pinned to cfe32b8be6)

Solutions

  1. Trim the offending member: remove packages/files from your gokrazy build until root.img/boot.img is under the printed limit.
  2. Use the official, unmodified Tailscale appliance GAF image which is guaranteed to fit the layout.
  3. If you genuinely need a bigger root, flash with a gokrazy-native tooling (gok overwrite / gokr-packer) that writes its own partition table instead of the fixed layout, or patch disklayout.RootPartitionSizeMB locally and rebuild the CLI.
  4. Check the printed byte counts to see which member is oversized and by how much before deciding.

Example fix

// before: oversized custom root
$ unzip -l my.gaf | grep root.img
  536870912  root.img   # larger than RootPartitionSizeMB<<20
$ tailscale configure flash-appliance --gaf my.gaf
Error: root.img is 536870912 bytes; gokrazy layout allows up to 524288000

// after: shrink build (remove large assets) and re-pack
gok build --update=all  # smaller root.img, then re-zip flat
$ tailscale configure flash-appliance --gaf smaller.gaf --disk /dev/disk2
Defensive patterns

Strategy: validation

Validate before calling

// check member sizes against the gokrazy layout limits before flashing
const bootMax = int64(disklayout.BootPartitionSizeMB) << 20
const rootMax = int64(disklayout.RootPartitionSizeMB) << 20

for _, ck := range []struct{ name string; max int64 }{
    {"boot.img", bootMax}, {"root.img", rootMax},
} {
    zf := findZipMember(files, ck.name)
    if zf == nil {
        return fmt.Errorf("GAF is missing %s", ck.name)
    }
    if int64(zf.UncompressedSize64) > ck.max {
        return fmt.Errorf("%s is %d bytes; gokrazy layout allows up to %d",
            ck.name, zf.UncompressedSize64, ck.max)
    }
}

Prevention

When it happens

Trigger: Calling `tailscale configure flash-appliance` with a custom-built GAF whose root.img (or boot.img) exceeds disklayout.RootPartitionSizeMB<<20 (resp. BootPartitionSizeMB<<20) bytes — e.g. you added packages/files to a gokrazy build and the squashfs root grew past the layout's fixed partition size.

Common situations: Custom gokrazy builds with extra packages or large assets, flashing an image built for a newer appliance layout with an older CLI whose disklayout constants are smaller, or accidentally zipping the wrong (bigger) artifact into the GAF.

Related errors


AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15). Data as JSON: /api/errors/23f006120b03f9e2. Report an issue: GitHub.