{"record":{"id":"e05718b55a86a924","repo":"chenhg5/cc-connect","slug":"get-access-token-w","errorCode":null,"errorMessage":"get access token: %w","messagePattern":"get access token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/card.go","lineNumber":63,"sourceCode":"\t_, _ = rand.Read(b)\n\treturn fmt.Sprintf(\"card_%d_%s\", time.Now().UnixMilli(), hex.EncodeToString(b))\n}\n\n// generateGUID generates a UUID-like string for API requests.\nfunc generateGUID() string {\n\tb := make([]byte, 16)\n\t_, _ = rand.Read(b)\n\t// Set version (4) and variant bits\n\tb[6] = (b[6] & 0x0f) | 0x40\n\tb[8] = (b[8] & 0x3f) | 0x80\n\treturn fmt.Sprintf(\"%x-%x-%x-%x-%x\", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16])\n}\n\n// createAICard creates a new AI Card instance and delivers it to the conversation.\nfunc (p *Platform) createAICard(ctx context.Context, rc replyContext) (*aiCard, error) {\n\ttoken, err := p.getAccessToken()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get access token: %w\", err)\n\t}\n\n\toutTrackId := generateOutTrackID()\n\tisGroup := rc.isGroup\n\n\t// Build openSpaceId based on conversation type\n\t// See: https://open.dingtalk.com/document/development/create-and-deliver-cards\n\tvar openSpaceId string\n\tif isGroup {\n\t\topenSpaceId = fmt.Sprintf(\"dtv1.card//IM_GROUP.%s\", rc.conversationId)\n\t} else {\n\t\topenSpaceId = fmt.Sprintf(\"dtv1.card//IM_ROBOT.%s\", rc.senderStaffId)\n\t}\n\n\t// Build card data\n\tcardParamMap := map[string]string{\n\t\t\"config\":          `{\"autoLayout\":true,\"enableForward\":true}`,\n\t\tp.cardTemplateKey: \"\",","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/card.go#L45-L81","documentation":"createAICard first acquires a DingTalk access token via p.getAccessToken(); any failure is wrapped as 'get access token: %w' and aborts creation of the streaming AI card. This is an authentication/credentials failure against the DingTalk Open API — the appKey/appSecret exchange or token fetch failed, so no card can be created.","triggerScenarios":"p.getAccessToken() returns an error: invalid or expired appKey/appSecret, DingTalk API returning an error (network failure, rate limit, non-2xx), missing credentials in config, or wrong endpoint.","commonSituations":"Wrong or rotated appSecret in config.toml for the dingtalk platform; DingTalk app disabled or permissions revoked; outbound network blocked to api.dingtalk.com (proxy/firewall); hitting DingTalk API rate limits; system clock skew invalidating cached token logic.","solutions":["Inspect the wrapped error: if it's an HTTP/auth error, re-check appKey and appSecret in the dingtalk config and regenerate the secret if rotated","Test outbound connectivity to DingTalk's token endpoint from the host (curl the API)","Verify the DingTalk app is enabled and has the AI-card/robot permissions granted in the developer console","Add retry with backoff for transient network/rate-limit failures in getAccessToken","Check host clock sync (NTP) if token caching/expiry logic is involved"],"exampleFix":"// before (config with rotated secret)\n[platforms.dingtalk]\nappKey = \"dingxxxx\"\nappSecret = \"old-secret\"\n// after\n[platforms.dingtalk]\nappKey = \"dingxxxx\"\nappSecret = \"newly-generated-secret\"","handlingStrategy":"fallback","validationCode":"// Pre-flight: verify credentials are present and fetch a token once at startup\nif cfg.AppKey == \"\" || cfg.AppSecret == \"\" {\n    return fmt.Errorf(\"dingtalk: appKey/appSecret required\")\n}\nif _, err := getAccessToken(); err != nil {\n    return fmt.Errorf(\"dingtalk: credential pre-flight failed: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"// Retry transient failures; abort on auth errors\nif _, err := p.getAccessToken(); err != nil {\n    if isAuthError(err) {\n        slog.Error(\"dingtalk: bad credentials; fix appKey/appSecret\", \"err\", err)\n        return err\n    }\n    return retryWithBackoff(ctx, p.getAccessToken, 3)\n}","preventionTips":["Rotate appSecret out-of-band and update config atomically","Grant the DingTalk app the AI-card/robot scopes in the developer console","Cache tokens and refresh before expiry with clock-skew margin","Pre-flight the token endpoint at startup and on a health timer"],"tags":["dingtalk","authentication","token","api"],"backgroundTag":"oauth-token-exchange-failed","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"}