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
- Check https://pkgs.tailscale.com/<track>/ for a GAF matching your variant (vm-amd64, vm-arm64, pi-arm64)
- Switch to the stable track if the GAF is missing on unstable/release-candidate
- If using a custom PkgsAddr mirror, mirror the whole track directory including GAFs and the listing JSON
- 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
- Verify the pkgs listing includes a GAF for your device variant before automating updates
- Prefer the stable track for gokrazy fleets
- Mirror GAF files as well as listing JSON when self-hosting PkgsAddr
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
- no GAF version on %q track
- gokrazy update support is not linked into this binary
- download GAF: %s
- GAF is missing %s
- PUT %s: %s: %s
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/00fd4fd41a1964ca.
Report an issue: GitHub.