{"record":{"id":"a5c59a7be1054580","repo":"getsops/sops","slug":"user-config-directory-could-not-be-determined-w","errorCode":null,"errorMessage":"user config directory could not be determined: %w","messagePattern":"user config directory could not be determined: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"age/keysource.go","lineNumber":456,"sourceCode":"\t}\n\n\tif ageKeyCmd, ok := os.LookupEnv(SopsAgeKeyCmdEnv); ok {\n\t\tout, err := getOutputFromCmd(ageKeyCmd, []string{fmt.Sprintf(\"%s=%s\", SopsAgeRecipientEnv, key.Recipient)})\n\t\tif err != nil {\n\t\t\terrs = append(errs, err)\n\t\t} else {\n\t\t\treaders[SopsAgeKeyCmdEnv] = identityReader{\n\t\t\t\treader:                   bytes.NewReader(out),\n\t\t\t\tallowMultipleKeysPerLine: false,\n\t\t\t}\n\t\t}\n\t} else {\n\t\tunusedLocations = append(unusedLocations, SopsAgeKeyCmdEnv)\n\t}\n\n\tuserConfigDir, err := getUserConfigDir()\n\tif err != nil && len(readers) == 0 && len(identities) == 0 {\n\t\terrs = append(errs, fmt.Errorf(\"user config directory could not be determined: %w\", err))\n\t} else if userConfigDir != \"\" {\n\t\tageKeyFilePath := filepath.Join(userConfigDir, filepath.FromSlash(SopsAgeKeyUserConfigPath))\n\t\tf, err := os.Open(ageKeyFilePath)\n\t\tif err != nil && !errors.Is(err, os.ErrNotExist) {\n\t\t\terrs = append(errs, fmt.Errorf(\"failed to open file: %w\", err))\n\t\t} else if errors.Is(err, os.ErrNotExist) && len(readers) == 0 && len(identities) == 0 {\n\t\t\tunusedLocations = append(unusedLocations, ageKeyFilePath)\n\t\t} else if err == nil {\n\t\t\tdefer f.Close()\n\t\t\treaders[ageKeyFilePath] = identityReader{\n\t\t\t\treader:                   f,\n\t\t\t\tallowMultipleKeysPerLine: false,\n\t\t\t}\n\t\t}\n\t}\n\n\tfor location, r := range readers {\n\t\tids, err := unwrapIdentities(location, r.reader, r.allowMultipleKeysPerLine)","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/age/keysource.go#L438-L474","documentation":"SOPS tries to fall back to the default age key at <userConfigDir>/sops/age/keys.txt, but os.UserConfigDir failed (XDG_CONFIG_HOME unset/unwritable on Linux, AppData missing on Windows) and no other key source was available, so this becomes a candidate-location error. It only surfaces when no identities were found via SOPS_AGE_KEY, SOPS_AGE_KEY_FILE, or SOPS_AGE_KEY_CMD.","triggerScenarios":"loadIdentities calls getUserConfigDir; getUserConfigDir returns an error from os.UserConfigDir, and readers/identities are both empty at that point, so the error is appended to errs.","commonSituations":"Containers or CI images with HOME and XDG_CONFIG_HOME unset; running sops as a different user (systemd service, cron) without a writable home; Windows machines with redirected/missing AppData.","solutions":["Set XDG_CONFIG_HOME (Linux/macOS) to a writable directory, e.g. export XDG_CONFIG_HOME=\"$HOME/.config\".","Ensure HOME is set for the user running sops (docker run -e HOME=/root, cron/systemd Environment=HOME=...).","Provide the identity explicitly via SOPS_AGE_KEY (inline) or SOPS_AGE_KEY_FILE so the config-dir fallback isn't needed.","Create the config directory: mkdir -p ~/.config/sops/age and place keys.txt there.","On Windows, verify %AppData% resolves in the environment running sops."],"exampleFix":"# before: bare container shell\n$ sops -d secrets.enc.yaml\n# user config directory could not be determined: $HOME is not defined\n\n# after\nexport HOME=/root\nexport XDG_CONFIG_HOME=\"$HOME/.config\"\nmkdir -p \"$XDG_CONFIG_HOME/sops/age\"\nmv keys.txt \"$XDG_CONFIG_HOME/sops/age/keys.txt\"","handlingStrategy":"validation","validationCode":"// shell: ensure sops can resolve a config dir, or supply the key directly\n: \"${XDG_CONFIG_HOME:=$HOME/.config}\"\nexport XDG_CONFIG_HOME\n[ -n \"${HOME:-}\" ] || { echo \"HOME must be set for sops\"; exit 1; }\nmkdir -p \"$XDG_CONFIG_HOME/sops/age\"","typeGuard":null,"tryCatchPattern":"if err := runSopsDecrypt(); err != nil &&\n   strings.Contains(err.Error(), \"user config directory could not be determined\") {\n    return fmt.Errorf(\"set XDG_CONFIG_HOME/HOME or provide SOPS_AGE_KEY directly: %w\", err)\n}","preventionTips":["Always set HOME (and XDG_CONFIG_HOME on Linux/macOS) in containers, cron, and systemd units.","Prefer explicit SOPS_AGE_KEY or SOPS_AGE_KEY_FILE in ephemeral environments so config-dir lookup never matters.","Create ~/.config/sops/age/keys.txt as part of onboarding docs/scripts.","For services, define Environment=HOME=/var/lib/<svc> in the unit file.","On Windows, don't override AppData in service contexts."],"tags":["age","config","env-var","platform"],"backgroundTag":"user-config-dir-missing","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}