{"record":{"id":"e32f95003ff68a6e","repo":"Tencent/WeKnora","slug":"parse-wechat-credentials-w","errorCode":null,"errorMessage":"parse wechat credentials: %w","messagePattern":"parse wechat credentials: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/wechat/factory.go","lineNumber":17,"sourceCode":"package wechat\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/Tencent/WeKnora/internal/im\"\n\t\"github.com/Tencent/WeKnora/internal/logger\"\n)\n\n// NewFactory returns an im.AdapterFactory for WeChat channels (iLink bot).\n// WeChat only supports a long-polling mode, so there is no mode branch.\nfunc NewFactory() im.AdapterFactory {\n\treturn func(factoryCtx context.Context, channel *im.IMChannel, msgHandler func(context.Context, *im.IncomingMessage) error) (im.Adapter, context.CancelFunc, error) {\n\t\tcreds, err := im.ParseCredentials(channel.Credentials)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"parse wechat credentials: %w\", err)\n\t\t}\n\n\t\tbotToken := im.GetString(creds, \"bot_token\")\n\t\tilinkBotID := im.GetString(creds, \"ilink_bot_id\")\n\n\t\tif botToken == \"\" || ilinkBotID == \"\" {\n\t\t\treturn nil, nil, fmt.Errorf(\"wechat credentials require bot_token and ilink_bot_id\")\n\t\t}\n\n\t\tadapter := NewAdapter(botToken, ilinkBotID)\n\t\tclient := NewLongPollClient(botToken, ilinkBotID, msgHandler)\n\n\t\tpollCtx, pollCancel := context.WithCancel(context.Background())\n\t\tgo func() {\n\t\t\tif err := client.Start(pollCtx); err != nil && pollCtx.Err() == nil {\n\t\t\t\tlogger.Errorf(context.Background(), \"[IM] WeChat long-poll stopped for channel %s: %v\", channel.ID, err)\n\t\t\t}\n\t\t}()","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/wechat/factory.go#L1-L35","documentation":"The WeChat factory parses the channel's Credentials JSON blob with im.ParseCredentials before extracting bot_token and ilink_bot_id. This error wraps any failure of that parse — the credentials field is missing, empty, or not valid JSON. The wrapped inner error carries the exact JSON failure reason.","triggerScenarios":"Creating a WeChat adapter (factory invocation with an IMChannel) whose channel.Credentials is empty, malformed JSON (e.g. trailing commas, single quotes), or a non-JSON value.","commonSituations":"Channel created without filling in the credentials JSON; manual DB edits writing invalid JSON; secrets stored as YAML or INI text pasted into the credentials field.","solutions":["Validate channel.Credentials with json.Valid / im.ParseCredentials before creating the adapter and fix the JSON.","Set credentials to a JSON object containing at least bot_token and ilink_bot_id, e.g. {\"bot_token\":\"...\",\"ilink_bot_id\":\"...\"}.","Log the inner wrapped error (fmt.Errorf %w chain) to identify the exact JSON syntax problem."],"exampleFix":"// before\nch.Credentials = \"bot_token=abc; ilink_bot_id=xyz\"\n// after\nch.Credentials = `{\"bot_token\":\"abc\",\"ilink_bot_id\":\"xyz\"}`","handlingStrategy":"validation","validationCode":"if !json.Valid([]byte(ch.Credentials)) {\n    return fmt.Errorf(\"wechat channel %d: credentials is not valid JSON\", ch.ID)\n}\nif _, err := im.ParseCredentials(ch.Credentials); err != nil {\n    return fmt.Errorf(\"wechat channel %d credentials: %w\", ch.ID, err)\n}","typeGuard":"func hasValidWeChatCreds(raw string) bool {\n    creds, err := im.ParseCredentials(raw)\n    return err == nil && creds != nil\n}","tryCatchPattern":"adapter, cancel, err := wechatFactory(ctx, ch, handler)\nif err != nil {\n    if strings.Contains(err.Error(), \"parse wechat credentials\") {\n        return fmt.Errorf(\"channel %d: credentials JSON invalid — %w\", ch.ID, err)\n    }\n    return err\n}","preventionTips":["Store credentials as marshalled JSON objects, never free-form text.","Run json.Valid on the credentials field in the channel-create handler.","Use a typed config struct marshalled to JSON to eliminate hand-written JSON errors."],"tags":["wechat","credentials","json-parsing"],"backgroundTag":"invalid-credentials-json","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}