{"record":{"id":"cfe796cb0bc9e6e0","repo":"bytebase/bytebase","slug":"failed-to-get-id-by-phone-errcode-d-errmsg-s","errorCode":null,"errorMessage":"failed to get id by phone, errcode: %d, errmsg: %s","messagePattern":"failed to get id by phone, errcode: (.+?), errmsg: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/webhook/dingtalk/app.go","lineNumber":97,"sourceCode":"\tif err != nil {\n\t\treturn \"\", errors.Wrapf(err, \"failed to get id by phone\")\n\t}\n\tvar response struct {\n\t\tErrcode int    `json:\"errcode\"`\n\t\tErrmsg  string `json:\"errmsg\"`\n\t\tResult  struct {\n\t\t\tUserID string `json:\"userid\"`\n\t\t} `json:\"result\"`\n\t}\n\tif err := json.Unmarshal(b, &response); err != nil {\n\t\treturn \"\", errors.Wrapf(err, \"failed to unmarshal response\")\n\t}\n\tif response.Errcode == 60121 {\n\t\tuserIDCache.Add(phone, \"\")\n\t\treturn \"\", nil\n\t}\n\tif response.Errcode != 0 {\n\t\treturn \"\", errors.Errorf(\"failed to get id by phone, errcode: %d, errmsg: %s\", response.Errcode, response.Errmsg)\n\t}\n\n\tuserIDCache.Add(phone, response.Result.UserID)\n\treturn response.Result.UserID, nil\n}\n\n// https://open.dingtalk.com/document/orgapp/chatbots-send-one-on-one-chat-messages-in-batches\nfunc (p *provider) sendMessage(ctx context.Context, userIDs []string, title, text string) error {\n\tconst url = \"https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend\"\n\tmarkdown, err := json.Marshal(struct {\n\t\tTitle string `json:\"title\"`\n\t\tText  string `json:\"text\"`\n\t}{\n\t\tTitle: title,\n\t\tText:  text,\n\t})\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to marshal markdown\")","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/webhook/dingtalk/app.go#L79-L115","documentation":"DingTalk returned a valid JSON response but with a non-zero errcode (other than 60121, which is treated as \"user not found\"); getIDByPhone converts it into errors.Errorf(\"failed to get id by phone, errcode: %d, errmsg: %s\"). This is an application-level API rejection from DingTalk, not a transport error.","triggerScenarios":"The getbymobile API responds with errcode != 0 and != 60121, e.g. errcode 60011 (no permission), 60020 (IP not in whitelist), 88 (invalid app key/secret), or other org/permission errors.","commonSituations":"The mobile number is not a member of the authorized org; the app lacks contact-read permission; server IP is not whitelisted for the DingTalk app; invalid or expired app credentials.","solutions":["Read the errcode/errmsg in the message and look it up in DingTalk's error code table","Grant the app contact/user read permissions in the DingTalk developer console if errcode is permission-related","Add the server's egress IP to the DingTalk app's IP whitelist (errcode 60020)","Fix appKey/appSecret if errcode indicates invalid credentials"],"exampleFix":"// before\n// app without contact read scope\n// after\n// DingTalk admin console -> app -> Permissions: enable \"Address book read\" (通讯录只读)","handlingStrategy":"fallback","validationCode":"// DingTalk errcode surfaces only after the call; pre-validate what you can:\nif appIPWhitelist != nil && !appIPWhitelist.Contains(serverEgressIP) {\n\treturn errors.New(\"server egress IP not in DingTalk app IP whitelist (errcode 60020)\")\n}","typeGuard":null,"tryCatchPattern":"id, err := p.getIDByPhone(ctx, phone)\nif err != nil {\n\tvar apiErr struct{ msg string }\n\tlog.Printf(\"dingtalk getbymobile rejected: %v\", err) // includes errcode/errmsg\n\treturn \"\", nil // degrade: send webhook without this mention\n}","preventionTips":["Grant the DingTalk app contact-read scope in the developer console before enabling mention lookups","Keep the server's egress IPs listed in the app's IP whitelist","Map common errcodes (60011, 60020, 60121, 88) to actionable messages for operators"],"tags":["dingtalk","api-error","errcode","permissions"],"backgroundTag":"api-error-response","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}