{"record":{"id":"1dbdf34940d43fed","repo":"Wei-Shaw/sub2api","slug":"failed-to-get-user-usage-trend","errorCode":null,"errorMessage":"failed to get user usage trend","messagePattern":"failed to get user usage trend","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"backend/internal/handler/admin/dashboard_snapshot_v2_handler.go","lineNumber":242,"sourceCode":"\t\t\tfilters.UserID,\n\t\t\tfilters.APIKeyID,\n\t\t\tfilters.AccountID,\n\t\t\tfilters.GroupID,\n\t\t\tfilters.RequestType,\n\t\t\tfilters.Stream,\n\t\t\tfilters.BillingType,\n\t\t\tfilters.UpstreamModelMismatch,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"failed to get group statistics\")\n\t\t}\n\t\tresp.Groups = groups\n\t}\n\n\tif includeUsersTrend {\n\t\tusersTrend, _, err := h.getUserUsageTrendCached(ctx, startTime, endTime, granularity, usersTrendLimit)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"failed to get user usage trend\")\n\t\t}\n\t\tresp.UsersTrend = usersTrend\n\t}\n\n\treturn resp, nil\n}\n\nfunc parseDashboardSnapshotV2Filters(c *gin.Context) (*dashboardSnapshotV2Filters, error) {\n\tfilters := &dashboardSnapshotV2Filters{\n\t\tModel: strings.TrimSpace(c.Query(\"model\")),\n\t}\n\n\tif userIDStr := strings.TrimSpace(c.Query(\"user_id\")); userIDStr != \"\" {\n\t\tid, err := strconv.ParseInt(userIDStr, 10, 64)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfilters.UserID = id","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/backend/internal/handler/admin/dashboard_snapshot_v2_handler.go#L224-L260","documentation":"Returned while assembling the v2 dashboard snapshot when includeUsersTrend is true and h.getUserUsageTrendCached(...) fails. Unlike the other sections it takes only granularity and a usersTrendLimit, so failures are almost purely storage-side (query error/timeout), not filter-related.","triggerScenarios":"GET dashboard snapshot v2 with the users-trend section included while the per-user trend aggregation over the window fails or times out.","commonSituations":"High user counts with fine granularity; a large usersTrendLimit forcing big per-user series; DB pressure from concurrent dashboards.","solutions":["Retry the request","Lower usersTrendLimit or use a coarser granularity","Shorten the date range","Check backend logs for the underlying user-trend query error"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Validate limit/granularity before the call\nif (!Number.isInteger(usersTrendLimit) || usersTrendLimit <= 0 || usersTrendLimit > 100) {\n  usersTrendLimit = 50;\n}","typeGuard":null,"tryCatchPattern":"// TS\ntry { usersTrend = (await getSnapshotV2({ includeUsersTrend: true, start, end, granularity, usersTrendLimit })).usersTrend; }\ncatch (e) {\n  if (e.message === 'failed to get user usage trend') {\n    usersTrend = (await withBackoff(() => getSnapshotV2({ includeUsersTrend: true, start, end, granularity, usersTrendLimit: 20 }))).usersTrend;\n  } else throw e;\n}","preventionTips":["Use a modest usersTrendLimit and coarse granularity for long ranges","Retry transient user-trend failures with backoff","Load dashboards lazily per section so a slow section does not block the page"],"tags":["dashboard","user-trend","database","admin"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}