{"record":{"id":"eb26930ee126c1b2","repo":"Billionmail/BillionMail","slug":"failed-to-convert-overview-data-v-eb2693","errorCode":null,"errorMessage":"failed to convert overview data: %v","messagePattern":"failed to convert overview data: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/overview/overview_v1_failed_list.go","lineNumber":32,"sourceCode":"func (c *ControllerV1) FailedList(ctx context.Context, req *v1.FailedListReq) (res *v1.FailedListRes, err error) {\n\tres = &v1.FailedListRes{}\n\n\tif req.EndTime == 0 {\n\t\treq.EndTime = time.Now().Unix()\n\t}\n\n\tif req.EndTime < req.StartTime {\n\t\terr = fmt.Errorf(\"start time must be greater or equal to end time\")\n\t\treturn\n\t}\n\n\toverview := maillog_stat.NewOverview()\n\tfailedList := overview.FailedList(req.CampaignId, req.Domain, req.StartTime, req.EndTime)\n\n\terr = gconv.Struct(failedList, &res.Data)\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"failed to convert overview data: %v\", err)\n\t\treturn\n\t}\n\n\tres.SetSuccess(public.LangCtx(ctx, \"Success\"))\n\n\treturn\n}\n","sourceCodeStart":14,"sourceCodeEnd":40,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/overview/overview_v1_failed_list.go#L14-L40","documentation":"After querying the overview statistics, FailedList uses goframe's gconv.Struct to map the service result into the response Data struct. If the conversion fails — mismatched field types, nil/incompatible structures — the raw conversion error is wrapped with this message.","triggerScenarios":"maillog_stat Overview().FailedList returning a shape (map or struct) whose fields cannot be converted into res.Data's type: e.g. numeric fields returned as strings that fail parsing, changed service return type after a refactor, or a nil result being converted.","commonSituations":"Schema drift between service and controller structs after code changes; DB returning NULLs into non-pointer numeric fields; version skew where an older service returns a different field set than the controller expects.","solutions":["Inspect the wrapped %v detail to see which field failed conversion","Align the service return struct and res.Data field names/types (use pointers for nullable DB columns)","Add unit tests covering the conversion with realistic service output","If the source data may be absent, guard for empty/nil before calling gconv.Struct"],"exampleFix":"// before\ntype failedItem struct { Count int `json:\"count\"` }\n// DB may return NULL\n// after\ntype failedItem struct { Count *int `json:\"count\"` }","handlingStrategy":"try-catch","validationCode":"// ensure expected fields are present in the response before conversion\nconst shapeOk = r && typeof r === 'object' && 'items' in r;","typeGuard":"function isFailedListShape(x: unknown): x is Record<string, unknown> {\n  return typeof x === 'object' && x !== null && !Array.isArray(x);\n}","tryCatchPattern":"try {\n  const data = await api.failedList(params);\n} catch (err) {\n  if (String(err.message).startsWith('failed to convert overview data')) {\n    console.error('Service/controller contract drift:', err);\n    // surface a backend-schema bug report\n  }\n}","preventionTips":["Keep service return structs and response DTOs in sync (shared types/tests)","Use pointer fields for nullable DB columns","Add conversion regression tests with real service output","Read the wrapped %v detail when debugging"],"tags":["goframe","type-conversion","serialization"],"backgroundTag":"struct-conversion-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}