{"record":{"id":"70f2bb6bb5c36364","repo":"unionlabs/union","slug":"failed-to-read-upgrade-info-from-disk-s","errorCode":null,"errorMessage":"failed to read upgrade info from disk %s","messagePattern":"failed to read upgrade info from disk (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"uniond/app/upgrades.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"fmt\"\n\n\tupgradetypes \"cosmossdk.io/x/upgrade/types\"\n\n\t\"github.com/unionlabs/union/uniond/app/upgrades\"\n\t\"github.com/unionlabs/union/uniond/app/upgrades/v1_1_0\"\n\t\"github.com/unionlabs/union/uniond/app/upgrades/v1_2_0\"\n\t\"github.com/unionlabs/union/uniond/app/upgrades/v1_3_0\"\n)\n\nvar Upgrades = []upgrades.Upgrade{v1_1_0.Upgrade, v1_2_0.Upgrade, v1_3_0.Upgrade}\n\n// configure store loader that checks if version == upgradeHeight and applies store upgrades\nfunc (app *App) setupUpgradeStoreLoaders() {\n\tupgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"failed to read upgrade info from disk %s\", err))\n\t}\n\n\tif app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) {\n\t\treturn\n\t}\n\n\tfor _, upgrade := range Upgrades {\n\t\tif upgradeInfo.Name == upgrade.UpgradeName {\n\t\t\tapp.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &upgrade.StoreUpgrades))\n\t\t}\n\t}\n}\n\nfunc (app *App) setupUpgradeHandlers() {\n\tfor _, upgrade := range Upgrades {\n\t\tapp.UpgradeKeeper.SetUpgradeHandler(\n\t\t\tupgrade.UpgradeName,\n\t\t\tupgrade.CreateUpgradeHandler(","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/uniond/app/upgrades.go#L2-L38","documentation":"At app construction, setupUpgradeStoreLoaders calls ReadUpgradeInfoFromDisk, which reads <home>/data/upgrade-info.json. A missing file is normal (empty info, no store loader set); an unreadable or malformed file makes the call return an error and the app panics before the node starts. The message embeds the underlying error via %s.","triggerScenarios":"upgrade-info.json exists but contains invalid/truncated JSON (interrupted upgrade-height write), the process cannot read the data dir (permissions/ownership), or --home points at a data dir whose upgrade-info.json is broken.","commonSituations":"Crash or power loss while the upgrade module wrote the file; running the node under a different user than the one that owns the home dir; hand-edited upgrade-info.json; wrong --home flag after a data-dir move.","solutions":["Inspect the file: jq . <home>/data/upgrade-info.json — the error's %s suffix tells you read vs parse failure","If JSON is malformed and no upgrade is actually pending, remove or rename the file and restart (a missing file is treated as no pending upgrade)","If an upgrade IS pending, fix the content to {\"name\":\"<plan>\",\"height\":N} rather than deleting it","Check permissions/ownership of the home dir and confirm the --home flag"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Pre-flight the file cosmos-sdk reads at startup\nf=\"$HOME/.union/data/upgrade-info.json\"\nif [ -f \"$f\" ]; then\n  jq -e 'has(\"name\") and has(\"height\")' \"$f\" >/dev/null || echo \"invalid upgrade-info.json — fix or remove before starting uniond\"\n  [ -r \"$f\" ] || echo \"upgrade-info.json not readable by this user\"\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-edit upgrade-info.json","Ensure the node process owns or can read <home>/data","Back up the data dir before scheduled upgrade heights","After a crash at an upgrade height, validate the file before restarting"],"tags":["cosmos-sdk","upgrade","startup","config","panic","golang"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}