{"record":{"id":"7d32e80aeb88ea4b","repo":"temporalio/temporal","slug":"failed-to-get-config-files-w","errorCode":null,"errorMessage":"failed to get config files: %w","messagePattern":"failed to get config files: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/config/loader.go","lineNumber":191,"sourceCode":"//\n//   base.yaml\n//     env.yaml     -- where \"environment\" is one of the input parameters (e.g., \"development\")\n//       env_az.yaml -- where \"zone\" is another input parameter\n\nfunc (opts *loadOptions) loadLegacy(config any) error {\n\tstdlog.Printf(\"Loading config; env=%v,zone=%v,configDir=%v\\n\", opts.env, opts.zone, opts.configDir)\n\tif opts.env == \"\" {\n\t\topts.env = envDevelopment\n\t}\n\tif opts.configDir == \"\" {\n\t\topts.configDir = defaultConfigDir\n\t}\n\n\tstdlog.Printf(\"Loading config; env=%v,zone=%v,configDir=%v\\n\", opts.env, opts.zone, opts.configDir)\n\n\tfiles, err := getConfigFiles(opts.env, opts.configDir, opts.zone)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get config files: %w\", err)\n\t}\n\n\tstdlog.Printf(\"Loading config files=%v\\n\", files)\n\n\tfor _, f := range files {\n\t\tdata, err := readConfigFile(f)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tprocessedData, err := processConfigFile(data, filepath.Base(f))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\terr = yaml.Unmarshal(processedData, config)\n\t\tif err != nil {\n\t\t\treturn err","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/config/loader.go#L173-L209","documentation":"loadLegacy in the config loader gathers the ordered list of YAML files (base, env, zone overlays) via getConfigFiles. If listing/reading the config directory fails (e.g. stat/read errors or malformed pattern), the error is wrapped as \"failed to get config files\" and config loading aborts. This means temporal could not even determine which config files exist, before parsing any of them.","triggerScenarios":"Calling LoadConfig/load -> loadLegacy with a configDir that does not exist, is unreadable (permissions), or where getConfigFiles encounters an OS error while resolving env/zone file paths.","commonSituations":"Wrong CONFIG_PATH/env var pointing at a missing directory, container image missing the /etc/temporal/config mount, permission denied after running as a non-root user, or typo'd configDir flag/zone name causing expected file paths to be unresolvable.","solutions":["Verify the config directory path (configDir option / CONFIG_PATH env) exists and is readable by the temporal process","Ensure config files follow the expected naming: base config, then env (and zone) files, e.g. development.yaml or production.yaml under the configured dir","Fix filesystem permissions (chown/chmod) or remount the config volume","Check the wrapped underlying error (%w) for the exact OS reason (no such file, permission denied, etc.)"],"exampleFix":"// before (cmd flag)\n--config-dir /etc/temporal/confg   # typo\n// after\n--config-dir /etc/temporal/config","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(configDir); err != nil || !info.IsDir() {\n\treturn fmt.Errorf(\"config dir %s missing or not a directory: %w\", configDir, err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the config dir exists in the deployment (volume mounts, image build) before startup","Standardize CONFIG_PATH across environments; fail fast with a readiness check","Mount config read-only and ensure the process user has traverse/read permissions"],"tags":["config","filesystem","startup"],"backgroundTag":"config-directory-not-found","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}