{"record":{"id":"5436f64124934e55","repo":"chenhg5/cc-connect","slug":"dingtalk-client-id-and-client-secret-are-required","errorCode":null,"errorMessage":"dingtalk: client_id and client_secret are required","messagePattern":"dingtalk: client_id and client_secret are required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":105,"sourceCode":"\treactionEmoji         string\n\tdoneEmoji             string\n\t// AI Card configuration\n\tcardTemplateID  string\n\tcardTemplateKey string\n\tcardThrottleMs  int\n\tdegradeUntil    time.Time\n\tdegradeMu       sync.Mutex\n}\n\nfunc New(opts map[string]any) (core.Platform, error) {\n\tclientID, _ := opts[\"client_id\"].(string)\n\tclientSecret, _ := opts[\"client_secret\"].(string)\n\trobotCode, _ := opts[\"robot_code\"].(string)\n\tallowFrom, _ := opts[\"allow_from\"].(string)\n\tcore.CheckAllowFrom(\"dingtalk\", allowFrom)\n\tshareSessionInChannel, _ := opts[\"share_session_in_channel\"].(bool)\n\tif clientID == \"\" || clientSecret == \"\" {\n\t\treturn nil, fmt.Errorf(\"dingtalk: client_id and client_secret are required\")\n\t}\n\tif robotCode == \"\" {\n\t\trobotCode = clientID // fallback to client_id if robot_code not specified\n\t}\n\t// Validate robot_code format (should not be empty after fallback)\n\tif robotCode == \"\" {\n\t\treturn nil, fmt.Errorf(\"dingtalk: robot_code is required (or client_id)\")\n\t}\n\n\treactionEmoji, _ := opts[\"reaction_emoji\"].(string)\n\treactionEmoji = strings.TrimSpace(reactionEmoji)\n\tif reactionEmoji == \"\" {\n\t\treactionEmoji = defaultReactionEmoji\n\t}\n\tif strings.EqualFold(reactionEmoji, \"none\") {\n\t\treactionEmoji = \"\"\n\t}\n\tdoneEmoji, _ := opts[\"done_emoji\"].(string)","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L87-L123","documentation":"The DingTalk platform constructor (New) validates that the required credentials are present in the options map before building the Platform. It throws this error when either the 'client_id' or 'client_secret' option is empty or not a string, because without them no API calls (access tokens, message sending) can be authenticated.","triggerScenarios":"Calling dingtalk.New(opts) where opts lacks 'client_id'/'client_secret' keys, they are set to empty strings, or they hold non-string values (the type assertion `opts[\"client_id\"].(string)` silently yields \"\" on wrong types).","commonSituations":"Config mistakes: copying a config.toml block and forgetting to fill in credentials; reading values from environment variables that are unset; passing YAML/JSON-decoded values where numbers or nested objects end up in place of strings; typos in option keys like 'clientId'.","solutions":["Set both 'client_id' and 'client_secret' string options in your dingtalk config to valid AppKey/AppSecret values from the DingTalk open platform.","Check that environment variables feeding these options are actually set and non-empty at startup.","Verify the option keys are exactly 'client_id' and 'client_secret' and the values are plain Go strings, not other types."],"exampleFix":"// before\np, err := dingtalk.New(map[string]any{\"client_id\": \"\", \"client_secret\": os.Getenv(\"DM_SECRET\")})\n// after\nclientID := os.Getenv(\"DINGTALK_CLIENT_ID\")\nclientSecret := os.Getenv(\"DINGTALK_CLIENT_SECRET\")\nif clientID == \"\" || clientSecret == \"\" { log.Fatal(\"dingtalk credentials missing\") }\np, err := dingtalk.New(map[string]any{\"client_id\": clientID, \"client_secret\": clientSecret})","handlingStrategy":"validation","validationCode":"func validateDingtalkOpts(opts map[string]any) error {\n    id, _ := opts[\"client_id\"].(string)\n    sec, _ := opts[\"client_secret\"].(string)\n    if id == \"\" || sec == \"\" {\n        return fmt.Errorf(\"dingtalk: need non-empty client_id and client_secret (got id=%q, secret set=%t)\", id, sec != \"\")\n    }\n    return nil\n}","typeGuard":"func optString(opts map[string]any, key string) (string, bool) {\n    s, ok := opts[key].(string)\n    return s, ok && s != \"\"\n}","tryCatchPattern":null,"preventionTips":["Load credentials from env at startup and fail fast if missing","Keep client_id/client_secret as strings; avoid nested config structs leaking into the options map","Use exact snake_case option keys","Add a startup smoke test that constructs the platform before serving"],"tags":["dingtalk","config","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}