chenhg5/cc-connect · error
empty downloadUrl in response
Error message
empty downloadUrl in response
What it means
getDownloadURL got a valid, decodable 200 response from the DingTalk media download API but the downloadUrl field is empty. The API accepted the request yet produced no usable URL, so there is nothing to fetch.
Source
Thrown at platform/dingtalk/dingtalk.go:717
req.Header.Set("x-acs-dingtalk-access-token", token)
resp, err := p.httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("do request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("api returned status %d", resp.StatusCode)
}
var result downloadResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", fmt.Errorf("decode response: %w", err)
}
if result.DownloadUrl == "" {
return "", fmt.Errorf("empty downloadUrl in response")
}
return result.DownloadUrl, nil
}
func (p *Platform) getAccessToken() (string, error) {
p.tokenMu.Lock()
defer p.tokenMu.Unlock()
// Return cached token if still valid
if p.accessToken != "" && time.Now().Before(p.tokenExpiry) {
return p.accessToken, nil
}
// Request new access token using DingTalk's new API (api.dingtalk.com/v1.0/oauth2/accessToken)
// This requires POST request with JSON body
url := "https://api.dingtalk.com/v1.0/oauth2/accessToken"
View on GitHub (pinned to 4000b2338a)
Solutions
- Ask the user to resend the image/file — the downloadCode is most likely expired or already consumed.
- Log the full response JSON to confirm the exact field names returned by your API version.
- Check the app's media download scope/permission in the DingTalk developer console.
- Upgrade cc-connect / verify against current DingTalk API docs in case the field was renamed.
- Treat this as retryable once after refreshing the access token, then fail the message.
Defensive patterns
Strategy: fallback
Validate before calling
if time.Since(mediaCodeReceivedAt) > 10*time.Minute {
// skip calling the API; ask user to resend
} Try / catch
url, err := p.getDownloadURL(ctx, code)
if err != nil {
if strings.Contains(err.Error(), "empty downloadUrl") {
return p.replyText(ctx, "The media has expired, please resend it.")
}
return err
} Prevention
- Process media promptly — stale codes yield empty URLs.
- Grant the app media-download permissions before deploying.
- Alert on this error pattern; it usually signals expired media or permission gaps.
- Provide a user-facing resend prompt as the fallback path.
When it happens
Trigger: handleImageMessage / handleFileMessage / downloadAudio receive a 200 response whose JSON body lacks a non-empty downloadUrl — e.g. the downloadCode is semantically invalid (expired/consumed) while the API still returns 200, or the media was deleted from DingTalk servers.
Common situations: Re-processing an old queued message whose media code expired, a DingTalk API change in field naming/casing, or the bot app lacking permission so the API returns a success envelope without a URL.
Understand the failure class
Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.
Related errors
- empty accessToken in response
- get access token: %w
- create AI card: status=%d, body=%s
- AI card delivery failed: %s
- stream AI card: status=%d, body=%s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/202023ba8e39762f.
Report an issue: GitHub.