{"record":{"id":"e21a24dffefa685a","repo":"semaphoreui/semaphore","slug":"internal-server-error","errorCode":null,"errorMessage":"Internal Server Error","messagePattern":"Internal Server Error","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"api/system_info.go","lineNumber":75,"sourceCode":"\t}\n\n\tif util.Config.Mfa.Email.Enabled {\n\t\tauthMethods.Email = &LoginEmailAuthMethod{}\n\t}\n\n\ttimezone := util.Config.Schedule.Timezone\n\n\tif timezone == \"\" {\n\t\ttimezone = \"UTC\"\n\t}\n\n\troles, err := helpers.Store(r).GetGlobalRoles()\n\tif err != nil {\n\t\tlog.WithFields(log.Fields{\n\t\t\t\"context\": \"system_info\",\n\t\t\t\"user_id\": user.ID,\n\t\t}).WithError(err).Error(\"Failed to get roles\")\n\t\thttp.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tvar plan string\n\n\ttoken, err := c.subscriptionService.GetToken()\n\n\tswitch {\n\tcase errors.Is(err, db.ErrNotFound):\n\t\terr = nil\n\t\tplan = \"\"\n\tcase err != nil:\n\t\tlog.WithFields(log.Fields{\n\t\t\t\"context\": \"system_info\",\n\t\t\t\"user_id\": user.ID,\n\t\t}).WithError(err).Error(\"Failed to get subscription plan\")\n\t\terr = nil\n\t\tplan = \"\"","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/api/system_info.go#L57-L93","documentation":"GetSystemInfo (api/system_info.go:75) returns 500 'Internal Server Error' when helpers.Store(r).GetGlobalRoles() fails — the database query for global roles errored. Details (user_id, error) go to the log under context 'system_info'; the client only sees the generic 500.","triggerScenarios":"GET the system-info endpoint when the backing store's GetGlobalRoles query fails: DB connection dropped, roles tables missing/not migrated, or store-level error.","commonSituations":"Database down or network partition between Semaphore and Postgres/MySQL; schema migrations not applied so the global-roles table is absent; read-only replica or permission issues for the DB user; transient connection pool exhaustion.","solutions":["Check the server log for 'Failed to get roles' with the underlying DB error.","Verify database connectivity and credentials; test with a direct query of the roles table.","Run pending database migrations (semaphore migrate / setup) to ensure role tables exist.","Restart the service once the DB is healthy and retry the endpoint."],"exampleFix":"# before: stale schema\n$ semaphore server  # GetGlobalRoles fails: relation \"roles\" does not exist\n// after: apply migrations first\n$ semaphore migrate\n$ semaphore server","handlingStrategy":"retry","validationCode":"// pre-flight: server health before calling system info\nconst health = await fetch('/api/ping');\nif (!health.ok) throw new Error(\"Backend/database unhealthy; skip system-info call\");","typeGuard":null,"tryCatchPattern":"try {\n  const info = await getSystemInfo();\n} catch (e) {\n  if (e.status === 500) {\n    await delay(backoff++ * 1000);\n    return getSystemInfoWithRetry(); // transient DB issues often resolve\n  }\n}","preventionTips":["Monitor database connectivity and alert before app-level 500s appear","Run schema migrations as part of every deployment","Use a store/DB user with SELECT on roles tables","Wrap system-info polling with bounded retry + backoff"],"tags":["database","system-info","http-500"],"backgroundTag":"database-query-failed","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}