{"record":{"id":"487eafaa2d71ec57","repo":"syncthing/syncthing","slug":"failed-to-load-config-unexpected-end-of-file-tru","errorCode":null,"errorMessage":"failed to load config: unexpected end of file. Truncated or empty configuration?","messagePattern":"failed to load config: unexpected end of file\\. Truncated or empty configuration\\?","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"lib/syncthing/utils.go","lineNumber":102,"sourceCode":"// LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it\n// creates a default one. Otherwise it checks the version, and archives and\n// upgrades the config if necessary or returns an error, if the version\n// isn't compatible.\nfunc LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logger, allowNewerConfig, skipPortProbing bool) (config.Wrapper, error) {\n\tmyID := protocol.NewDeviceID(cert.Certificate[0])\n\tcfg, originalVersion, err := config.Load(path, myID, evLogger)\n\tif fs.IsNotExist(err) {\n\t\tcfg, err = DefaultConfig(path, myID, evLogger, skipPortProbing)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to generate default config: %w\", err)\n\t\t}\n\t\terr = cfg.Save()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to save default config: %w\", err)\n\t\t}\n\t\tslog.Info(\"Default config saved; edit to taste (with Syncthing stopped) or use the GUI\", slogutil.FilePath(cfg.ConfigPath()))\n\t} else if errors.Is(err, io.EOF) {\n\t\treturn nil, errors.New(\"failed to load config: unexpected end of file. Truncated or empty configuration?\")\n\t} else if err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load config: %w\", err)\n\t}\n\n\tif originalVersion != config.CurrentVersion {\n\t\tif originalVersion > config.CurrentVersion && !allowNewerConfig {\n\t\t\treturn nil, fmt.Errorf(\"config file version (%d) is newer than supported version (%d); if this is expected, use --allow-newer-config to override\", originalVersion, config.CurrentVersion)\n\t\t}\n\t\terr = archiveAndSaveConfig(cfg, originalVersion)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"config archive: %w\", err)\n\t\t}\n\t}\n\n\treturn cfg, nil\n}\n\nfunc archiveAndSaveConfig(cfg config.Wrapper, originalVersion int) error {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/syncthing/syncthing/blob/058bcd7334839663cf569501d3ac539034d45cb5/lib/syncthing/utils.go#L84-L120","documentation":"config.Load returned io.EOF, meaning the configuration XML file exists but contains zero bytes or is cut off mid-document. Syncthing refuses to fabricate a config in this case (unlike a missing file, which triggers DefaultConfig generation) because overwriting might destroy a partially-written real config.","triggerScenarios":"Loading a config path where the file exists but is empty (0 bytes), or was truncated by a crash/power loss during Save. Triggered via lib/syncthing/utils.go LoadConfig at startup when config.Load's xml decode hits EOF before any element.","commonSituations":"Power loss or SIGKILL during a config write (Syncthing writes atomically, but external tools may not), an editor that emptied the file, a failed 'syncthing generate' run, or a disk-full event leaving a zero-length config.xml.","solutions":["Restore config.xml from the .stbackup archive copies or your backup, then restart","If no backup exists and the file is truly empty/unwanted, delete config.xml so DefaultConfig regeneration kicks in (Syncthing will create and save a default config)","Check the same directory for numbered/backup config versions (config.xml.v0/v1... or *.stbackup) and rename one to config.xml","Investigate why the file was truncated (disk full, crash) to prevent recurrence"],"exampleFix":"# before: config.xml is 0 bytes, startup fails\n$ ls -l config.xml   # -rw-r--r-- 0 bytes\n\n# after: remove so a default is generated (or restore a backup)\nmv config.xml config.xml.broken\ncp config.xml.v14 config.xml   # or just start syncthing to regenerate default","handlingStrategy":"validation","validationCode":"// Before startup, check the config is non-trivial\nfi, err := os.Stat(cfgPath)\nif err == nil && fi.Size() == 0 {\n    return fmt.Errorf(\"config %s is empty; restore from backup or remove to regenerate\", cfgPath)\n}","typeGuard":null,"tryCatchPattern":"// Go: distinguish EOF-truncation from other load errors\nif err := syncthing.LoadConfig(path, id, logger, false, false); err != nil {\n    if strings.Contains(err.Error(), \"unexpected end of file\") {\n        // restore last backup before retrying once\n    }\n}","preventionTips":["Back up the config directory regularly — Syncthing keeps archived copies; know where they are","Avoid external tools that rewrite config.xml non-atomically","Monitor free disk space; truncation is often a disk-full casualty"],"tags":["config","startup","filesystem"],"backgroundTag":null,"analyzedSha":"058bcd7334839663cf569501d3ac539034d45cb5","analyzedAt":"2026-08-15T07:53:43.174Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}