juanfont/headscale · error
loading config: %w
Error message
loading config: %w
What it means
Thrown by bypassDatabase() in the policy CLI commands when types.LoadServerConfig() fails. Bypass mode (--bypass-server-and-access-database-directly) opens the database directly, which first requires loading the server configuration file from disk. The wrapped error names the config file and the parse failure.
Source
Thrown at cmd/headscale/cli/policy.go:29
"github.com/juanfont/headscale/hscontrol/db"
"github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/spf13/cobra"
"tailscale.com/types/views"
)
const (
bypassFlag = "bypass-server-and-access-database-directly" //nolint:gosec // not a credential
)
var errAborted = errors.New("command aborted by user")
// bypassDatabase opens the database directly, bypassing the running server.
// The caller must close the returned handle.
func bypassDatabase() (*db.HSDatabase, error) {
cfg, err := types.LoadServerConfig()
if err != nil {
return nil, fmt.Errorf("loading config: %w", err)
}
d, err := db.NewHeadscaleDatabase(cfg)
if err != nil {
return nil, fmt.Errorf("opening database: %w", err)
}
return d, nil
}
// openBypassDB confirms the destructive bypass action and opens the database
// directly. The caller is responsible for closing the returned handle.
func openBypassDB(cmd *cobra.Command) (*db.HSDatabase, error) {
if !confirmAction(cmd, "DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?") {
return nil, errAborted
}
return bypassDatabase()View on GitHub (pinned to 565fd254d0)
Solutions
- Pass the config explicitly: `headscale --config /etc/headscale/config.yaml policy ...` or set HEADSCALE_CONFIG_PATH.
- Read the wrapped error — it names the exact file and YAML problem (line/column).
- Validate the file parses: `headscale config view` (non-bypass path uses the same loader).
- If keys were renamed in an upgrade, re-check the config reference in docs/ for your version.
Example fix
# before headscale policy get --bypass-server-and-access-database-directly # after headscale --config /etc/headscale/config.yaml policy get --bypass-server-and-access-database-directly
Defensive patterns
Strategy: validation
Validate before calling
// before running a bypass command, assert the config exists and parses
func checkConfig(path string) error {
fi, err := os.Stat(path)
if err != nil {
return fmt.Errorf("config missing: %w", err)
}
if fi.IsDir() {
return fmt.Errorf("config path is a directory: %s", path)
}
return nil
} Try / catch
if _, err := os.Stat(cfgPath); err != nil {
log.Fatalf("config check failed: %v — pass --config explicitly", err)
} Prevention
- Always invoke the CLI with an explicit --config in scripts and cron jobs.
- Set HEADSCALE_CONFIG_PATH in service definitions.
- After upgrading headscale, run a config-parse smoke test (headscale config view) before automation depends on it.
When it happens
Trigger: Running `headscale policy get/set/check --bypass-server-and-access-database-directly` when the configuration file does not exist at the expected path, is unreadable, or contains invalid YAML. Also triggered by config keys with invalid values (bad duration, unknown enum).
Common situations: Running the CLI from a directory other than where headscale config lives without setting the config path flag/env; config migrated between headscale versions with renamed keys; permissions on /etc/headscale/config.yaml; leftover BOM or tabs breaking YAML parsing.
Related errors
- opening database: %w
- loading policy from database: %w
- loading users for policy validation: %w
- setting ACL policy: %w
- loading users: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/92e2fd13e4d5850f.
Report an issue: GitHub.