{"record":{"id":"2d72cfb5e5313155","repo":"wavetermdev/waveterm","slug":"error-updating-mainserver-w","errorCode":null,"errorMessage":"error updating mainserver: %w","messagePattern":"error updating mainserver: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/wcore/wcore.go","lineNumber":195,"sourceCode":"\t} else if err != nil {\n\t\treturn fmt.Errorf(\"error getting mainserver: %w\", err)\n\t}\n\n\tneedsUpdate := false\n\tif mainServer.JwtPrivateKey == \"\" || mainServer.JwtPublicKey == \"\" {\n\t\tkeyPair, err := wavejwt.GenerateKeyPair()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error generating jwt keypair: %w\", err)\n\t\t}\n\t\tmainServer.JwtPrivateKey = base64.StdEncoding.EncodeToString(keyPair.PrivateKey)\n\t\tmainServer.JwtPublicKey = base64.StdEncoding.EncodeToString(keyPair.PublicKey)\n\t\tneedsUpdate = true\n\t}\n\n\tif needsUpdate {\n\t\terr = wstore.DBUpdate(ctx, mainServer)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error updating mainserver: %w\", err)\n\t\t}\n\t}\n\n\tprivateKeyBytes, err := base64.StdEncoding.DecodeString(mainServer.JwtPrivateKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error decoding jwt private key: %w\", err)\n\t}\n\tpublicKeyBytes, err := base64.StdEncoding.DecodeString(mainServer.JwtPublicKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error decoding jwt public key: %w\", err)\n\t}\n\n\terr = wavejwt.SetPrivateKey(privateKeyBytes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error setting jwt private key: %w\", err)\n\t}\n\terr = wavejwt.SetPublicKey(publicKeyBytes)\n\tif err != nil {","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wcore/wcore.go#L177-L213","documentation":"After generating new JWT keys (or otherwise marking the MainServer singleton dirty), InitMainServer persists it with wstore.DBUpdate. This error wraps a failed update, meaning the newly generated keys could not be saved to the database.","triggerScenarios":"wstore.DBUpdate(ctx, mainServer) returns an error during InitMainServer — SQLite write failure (disk full, read-only filesystem, DB lock held by another process, corruption), or the 5-second context deadline expiring mid-write.","commonSituations":"Disk quota/full disk on first run; ~/.waveterm on a read-only mount; concurrent Wave processes locking the DB; crash-damaged database.","solutions":["Free disk space / fix quota, and ensure the DB directory is writable","Check the wrapped cause for 'database is locked' and close other Wave instances","Verify the filesystem is not mounted read-only (especially in containers)","If the DB is corrupt, back up and remove wave.db so a fresh DB and keys are created"],"exampleFix":"// before (container)\nvolumes: [\"./wave:/root/.waveterm:ro\"]\n// after\nvolumes: [\"./wave:/root/.waveterm:rw\"]","handlingStrategy":"validation","validationCode":"// before startup: verify DB is writable\nf, err := os.OpenFile(dbPath, os.O_RDWR, 0o600)\nif err != nil {\n    log.Fatalf(\"wave.db not writable: %v\", err)\n}\nf.Close()","typeGuard":null,"tryCatchPattern":"if err := wcore.InitMainServer(); err != nil {\n    if strings.Contains(err.Error(), \"readonly\") || strings.Contains(err.Error(), \"locked\") {\n        log.Fatalf(\"fix db mount/locks before retrying: %v\", err)\n    }\n    panic(err)\n}","preventionTips":["Mount the config/db directory read-write in containers","Keep free disk space above a minimum threshold","Run a single app instance per database file","Avoid killing the process mid-write to prevent corruption"],"tags":["database","persistence","jwt","write-failure"],"backgroundTag":"database-write-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}