gotify/server · error

read file for %s_FILE (%s): %w

Error message

read file for %s_FILE (%s): %w

What it means

lookupEnv supports the _FILE convention: if <ENV>_FILE is set, the actual value is read from that file. If os.ReadFile fails, lookupEnv returns the wrapped error 'read file for %s_FILE (%s): %w', which propagates out of config.Get for every parser (parseString, parseInt, parseBool, parseList, parseMap, parseLogLevel).

Source

Thrown at config/parse.go:22

	"encoding/csv"
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func lookupEnv(env string) (string, bool, error) {
	if raw, ok := os.LookupEnv(env); ok {
		return raw, true, nil
	}
	path, ok := os.LookupEnv(env + "_FILE")
	if !ok {
		return "", false, nil
	}
	data, err := os.ReadFile(path)
	if err != nil {
		return "", false, fmt.Errorf("read file for %s_FILE (%s): %w", env, path, err)
	}
	return strings.TrimRight(string(data), "\r\n"), true, nil
}

func parseString(target *string, env string) error {
	raw, ok, err := lookupEnv(env)
	if err != nil {
		return err
	}
	if ok {
		*target = raw
	}
	return nil
}

func parseInt(target *int, env string) error {
	raw, ok, err := lookupEnv(env)
	if err != nil {

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Verify the file path in <VAR>_FILE exists and is readable inside the runtime environment (ls -l, check the mount).
  2. Fix the Kubernetes/Docker volume mount so the secret is present at the exact path referenced by _FILE.
  3. Use an absolute path for _FILE values.
  4. Handle the error from config.Get at startup and fail fast with a message naming the variable and path.

Example fix

// before
API_KEY_FILE=/run/secrets/api_key  # file not mounted
// after
API_KEY_FILE=/run/secrets/api-key  # matches the actual volume mount path
Defensive patterns

Strategy: validation

Validate before calling

func assertEnvFileReadable(env string) error {
    p, ok := os.LookupEnv(env + "_FILE")
    if !ok { return nil }
    f, err := os.Open(p)
    if err != nil {
        return fmt.Errorf("%s_FILE=%s is not readable: %w", env, p, err)
    }
    return f.Close()
}
// call before config.Get: assertEnvFileReadable("API_KEY")

Try / catch

if err := config.Get(&cfg); err != nil {
    if m := fileVarRe.FindStringSubmatch(err.Error()); m != nil {
        return fmt.Errorf("check secret mount for %s: %w", m[1], err)
    }
    return err
}

Prevention

When it happens

Trigger: Setting an env var like API_KEY_FILE to a path that does not exist or is unreadable, then calling config.Get; the error surfaces on whichever var uses the _FILE mechanism.

Common situations: Docker/Kubernetes secrets mounted at a different path than _FILE points to, secret not mounted (typo in volume mount), file deleted at container start, or relative path resolved against the wrong working directory.

Related errors


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/d9964e31d7ae2a6c. Report an issue: GitHub.