{"record":{"id":"bcccab1bf5cfea60","repo":"gotify/server","slug":"cannot-read-config-file-s-w","errorCode":null,"errorMessage":"cannot read config file %s: %w","messagePattern":"cannot read config file (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/migrate/migrate.go","lineNumber":80,"sourceCode":"\tOIDC              struct {\n\t\tEnabled       *bool\n\t\tIssuer        *string\n\t\tClientID      *string\n\t\tClientSecret  *string\n\t\tUsernameClaim *string\n\t\tRedirectURL   *string\n\t\tAutoRegister  *bool\n\t\tScopes        []string\n\t}\n}\n\nfunc Config(file string) (string, error) {\n\tif file == \"\" {\n\t\treturn \"\", errors.New(\"migrate-config requires one argument: the path to the old config.yml\")\n\t}\n\tdata, err := os.ReadFile(file)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot read config file %s: %w\", file, err)\n\t}\n\n\tvar migrated oldConfig\n\tif err := yaml.Unmarshal(data, &migrated); err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot parse config file %s: %w\", file, err)\n\t}\n\n\tcontent, err := godotenv.Marshal(buildEnv(migrated))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot render config: %w\", err)\n\t}\n\n\treturn content, nil\n}\n\nfunc buildEnv(c oldConfig) map[string]string {\n\tout := map[string]string{}\n\tstr := func(key string, value *string) {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/config/migrate/migrate.go#L62-L98","documentation":"config/migrate.Config reads the old YAML config file given on the command line so it can be converted to the new .env format. If os.ReadFile fails (missing path, permission denied, path is a directory), the OS error is wrapped as 'cannot read config file %s: %w' and returned to the migrate command.","triggerScenarios":"Running the migrate-config command (run/runMigrate) with a path that does not exist, a path the process cannot read (permissions), or a directory instead of a file; also empty-file argument is rejected earlier with a different error.","commonSituations":"Typo in the config.yml path, running the migration from a different working directory with a relative path, Docker container not mounting the old config, or file owned by another user after deployment.","solutions":["Check the path exists and is a regular file: ls -l <path>; fix typos or pass an absolute path.","Fix permissions so the process user can read the file (chmod/chown or run as the right user).","In containers, verify the old config is volume-mounted into the container at the given path.","Handle the returned error in runMigrate by printing a clear message with the wrapped os error (e.g. os.IsNotExist) to guide the operator."],"exampleFix":"// before\nmigrated, err := migrate.Config(\"config.old.yml\") // relative path, wrong cwd\n// after\nmigrated, err := migrate.Config(\"/etc/app/config.old.yml\") // absolute, verified path","handlingStrategy":"validation","validationCode":"func assertReadable(path string) error {\n    info, err := os.Stat(path)\n    if err != nil {\n        return fmt.Errorf(\"old config %s: %w\", path, err)\n    }\n    if info.IsDir() {\n        return fmt.Errorf(\"%s is a directory, want a file\", path)\n    }\n    f, err := os.Open(path)\n    if err != nil { return err }\n    return f.Close()\n}","typeGuard":null,"tryCatchPattern":"out, err := migrate.Config(oldPath)\nif err != nil {\n    if errors.Is(err, fs.ErrNotExist) {\n        return fmt.Errorf(\"old config not found at %s\", oldPath)\n    }\n    if errors.Is(err, fs.ErrPermission) {\n        return fmt.Errorf(\"cannot read %s: check permissions\", oldPath)\n    }\n    return err\n}","preventionTips":["Use absolute paths for the old config when scripting the migration","Check file readability with the same user that runs the migrate command","In containers, verify the config volume mount before running migrate-config","Keep a dry-run flag that stats the file before the real migration"],"tags":["go","config-migration","file-io","cli"],"backgroundTag":"config-file-not-found","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}