{"record":{"id":"44185b7daedc47f0","repo":"wavetermdev/waveterm","slug":"error-getting-mainserver-w","errorCode":null,"errorMessage":"error getting mainserver: %w","messagePattern":"error getting mainserver: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/wcore/wcore.go","lineNumber":178,"sourceCode":"\t\t}\n\t}()\n}\n\nfunc InitMainServer() error {\n\tctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancelFn()\n\n\tmainServer, err := wstore.DBGetSingleton[*waveobj.MainServer](ctx)\n\tif err == wstore.ErrNotFound {\n\t\tmainServer = &waveobj.MainServer{\n\t\t\tOID: uuid.NewString(),\n\t\t}\n\t\terr = wstore.DBInsert(ctx, mainServer)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error inserting mainserver: %w\", err)\n\t\t}\n\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}","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wcore/wcore.go#L160-L196","documentation":"InitMainServer loads the MainServer singleton from the local wstore database. If DBGetSingleton returns any error other than ErrNotFound (which means 'create a new one'), this error wraps it and aborts startup. It indicates the main server record could not be read from the underlying store.","triggerScenarios":"Calling wstore.DBGetSingleton[*waveobj.MainServer](ctx) inside InitMainServer returns a non-ErrNotFound error — e.g. the SQLite wave.db file is corrupted, locked by another process, unreadable due to file permissions, or the 5-second context times out mid-query.","commonSituations":"Corrupt or partially-migrated wave database after a crash or version upgrade; database file owned by another user after running with sudo; disk full; two Wave instances contending for the same DB lock.","solutions":["Check the wrapped cause (`%w` chain) printed in logs for a SQLite 'database is locked'/'disk I/O error' and fix the underlying storage issue","Verify file permissions on the Wave config/db directory (~/.waveterm) and that no other Wave process is running","Back up and remove/rename the wave.db database so a fresh singleton is created on next start (loses stored server state)","Ensure the app version's DB schema matches the existing DB; run the app's migration path instead of downgrading"],"exampleFix":"// before (diagnose)\nerr := wcore.InitMainServer()\nlog.Fatal(err)\n// after\nif err := wcore.InitMainServer(); err != nil {\n    log.Printf(\"init failed: %v\", errors.Unwrap(err)) // see real DB cause\n    os.Exit(1)\n}","handlingStrategy":"try-catch","validationCode":"// before startup\nif _, err := os.Stat(dbPath); err != nil {\n    log.Printf(\"db missing, fresh start expected: %v\", err)\n}\nif err := checkWritableDir(dbDir); err != nil {\n    log.Fatalf(\"db dir not writable: %v\", err)\n}","typeGuard":"func isDBLockedErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"database is locked\")\n}","tryCatchPattern":"if err := wcore.InitMainServer(); err != nil {\n    if isDBLockedErr(err) {\n        time.Sleep(500 * time.Millisecond)\n        return wcore.InitMainServer() // retry once\n    }\n    log.Fatalf(\"mainserver init: %v\", err)\n}","preventionTips":["Ensure only one Wave instance runs against a given wave.db","Keep the DB directory writable and with adequate disk space","Never run the app with mixed users (sudo vs normal) on the same DB","Monitor the wrapped cause via errors.Unwrap to distinguish lock vs corruption"],"tags":["database","startup","sqlite","initialization"],"backgroundTag":"database-read-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}