glanceapp/glance · error
reading secret file: %v
Error message
reading secret file: %v
What it means
Thrown by parseConfigVariableOfType for {{ secret.NAME }} expressions when os.ReadFile of /run/secrets/NAME fails (missing file, permission denied, directory absent). Docker's secrets mechanism mounts files under /run/secrets; the value used is the trimmed file contents. The underlying fs error is wrapped with 'reading secret file:'.
Source
Thrown at internal/glance/config.go:207
// When the bool return value is true, it indicates that the caller should use the original value
func parseConfigVariableOfType(variableType, variableName string) (string, bool, error) {
switch variableType {
case configVarTypeEnv:
if !envVariableNamePattern.MatchString(variableName) {
return "", true, nil
}
v, found := os.LookupEnv(variableName)
if !found {
return "", false, fmt.Errorf("environment variable %s not found", variableName)
}
return v, false, nil
case configVarTypeSecret:
secretPath := filepath.Join("/run/secrets", variableName)
secret, err := os.ReadFile(secretPath)
if err != nil {
return "", false, fmt.Errorf("reading secret file: %v", err)
}
return strings.TrimSpace(string(secret)), false, nil
case configVarTypeFileFromEnv:
if !envVariableNamePattern.MatchString(variableName) {
return "", true, nil
}
filePath, found := os.LookupEnv(variableName)
if !found {
return "", false, fmt.Errorf("readFileFromEnv: environment variable %s not found", variableName)
}
if !filepath.IsAbs(filePath) {
return "", false, fmt.Errorf("readFileFromEnv: file path %s is not absolute", filePath)
}
fileContents, err := os.ReadFile(filePath)View on GitHub (pinned to 91324e8de7)
Solutions
- Ensure the file exists at exactly /run/secrets/NAME with the secret as its contents (Docker secrets or manual placement: mkdir -p /run/secrets && echo -n 'value' > /run/secrets/NAME).
- Verify the process user can read the file (chmod 644 / appropriate ownership).
- Check the name inside {{ secret.X }} matches the mounted secret's filename exactly.
- In compose, declare and map the secret: secrets: [db_password] under the service plus top-level secrets.
- Read the wrapped error — it tells you whether it was 'no such file' vs 'permission denied'.
Example fix
# docker-compose.yml (before)
services:
glance:
configs:
- glance.yml # secret never mounted
# after
services:
glance:
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt Defensive patterns
Strategy: validation
Validate before calling
# pre-flight check for every {{ secret.X }} in glance.yml
mkdir -p /run/secrets
for s in $(grep -oE '\{\{ *secret\.[A-Za-z0-9_-]+ *\}\}' glance.yml | sed -E 's/.*secret\.([A-Za-z0-9_-]+).*/\1/'); do
[ -r "/run/secrets/$s" ] || { echo "secret not readable: /run/secrets/$s"; exit 1; }
done Prevention
- Map Docker/compose secrets so files land under /run/secrets/<name>.
- Make secret files readable by the Glance process user.
- Prefer {{ secret.X }} over baking values into glance.yml.
When it happens
Trigger: glance.yml contains {{ secret.db_password }} but /run/secrets/db_password does not exist on the Glance host, the process lacks read permission, or /run/secrets itself is absent (non-Docker or non-swarm deployment without manually placing the file).
Common situations: Running the Docker image without `--secret` / compose `secrets:` mapping; running the binary directly on a host where the secrets dir was never created; file owned by root while Glance runs as a non-root user; secret name typo.
Related errors
- readFileFromEnv: reading file from %s: %v
- parsing variable: %v
- readFileFromEnv: environment variable %s not found
- assets directory does not exist: %s
- fetching containers: %w
AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15).
Data as JSON: /api/errors/9252c90cabcd63cc.
Report an issue: GitHub.