tailscale/tailscale · error

no GAF for device %q on %q track

Error message

no GAF for device %q on %q track

What it means

The gokrazy update path (updateGokrazy) resolves the device variant via gokrazyDeviceVariant() (vm-amd64, vm-arm64, or pi-arm64) and looks it up in the GAFs map of the TrackPackages returned by LatestPackages(up.Track); if the track's package listing has no GAF for that variant, this error names the variant and the track. Nothing has been downloaded or applied.

Source

Thrown at clientupdate/clientupdate.go:915

// updateGokrazy fetches the latest signed GAF for this gokrazy device variant
// (vm-amd64, vm-arm64, or pi-arm64) from up.PkgsAddr and applies it via the
// local gokrazy init update API.
func (up *Updater) updateGokrazy() error {
	if !GokrazyUpdateFromURL.IsSet() {
		return errors.New("gokrazy update support is not linked into this binary")
	}
	variant, err := gokrazyDeviceVariant()
	if err != nil {
		return err
	}
	latest, err := LatestPackages(up.Track)
	if err != nil {
		return err
	}
	gafName, ok := latest.GAFs[variant]
	if !ok {
		return fmt.Errorf("no GAF for device %q on %q track", variant, up.Track)
	}
	if latest.GAFsVersion == "" {
		return fmt.Errorf("no GAF version on %q track", up.Track)
	}
	if !up.confirm(latest.GAFsVersion) {
		return nil
	}
	gafURL := fmt.Sprintf("%s/%s/%s", strings.TrimRight(up.PkgsAddr, "/"), up.Track, gafName)
	up.Logf("Updating to %s (%s)", latest.GAFsVersion, gafURL)
	return GokrazyUpdateFromURL.Get()(context.Background(), GokrazyUpdateArgs{
		URL:  gafURL,
		Logf: up.Logf,
	})
}

// gokrazyDeviceVariant returns the GAFs JSON key for the current gokrazy
// device, e.g. "vm-amd64", "vm-arm64", or "pi-arm64". On arm64, it reads the
// device-tree model to tell a Raspberry Pi apart from a VM.

View on GitHub (pinned to cfe32b8be6)

Solutions

  1. Check https://pkgs.tailscale.com/<track>/ for a GAF matching your variant (vm-amd64, vm-arm64, pi-arm64)
  2. Switch to the stable track if the GAF is missing on unstable/release-candidate
  3. If using a custom PkgsAddr mirror, mirror the whole track directory including GAFs and the listing JSON
  4. If the variant is genuinely unsupported, update the gokrazy image via gokrazy's own update mechanism instead
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the variant and check the track listing before updating.
variant := "vm-arm64" // resolve per device like gokrazyDeviceVariant()
latest, err := clientupdate.LatestPackages(track)
if err != nil {
    return err
}
if _, ok := latest.GAFs[variant]; !ok {
    log.Printf("no GAF for %s on %s track; skipping update", variant, track)
    return nil
}

Prevention

When it happens

Trigger: Calling the update flow on a gokrazy device whose variant key is absent from the track's packages listing - e.g. a pi-arm64 device on a track/moment where only vm-* GAFs are published, or a mirrored listing that omits the variant's GAF file.

Common situations: A track was just switched or a new device class is not yet published for that track; a self-hosted PkgsAddr mirror that did not sync GAF files; a listing schema change on the packages server.

Related errors


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