这个错误 space of card is empty 是钉钉开放平台在 2023-2024 年架构升级 后引入的典型错误。
🔴 错误原因
钉钉现在的卡片消息机制发生了重大变化:卡片模板(Card Template)不再内嵌在 API 请求的 JSON body 中,而是必须预先在钉钉后台创建好,并获得一个唯一的 cardKey。
旧模式:发送消息时,把整个卡片的 JSON 结构(标题、按钮、布局)直接发给接口。
新模式:
先在钉钉开发者后台设计卡片,生成模板,拿到
cardKey。调用 API 时,只需传入这个
cardKey和动态数据 (cardData)。如果你还在用旧模式(请求体里没有
cardKey,或者cardKey为空),服务端解析不到模板标识,就会报space of card is empty。
✅ 解决方案 (三步走)
第一步:在钉钉后台创建卡片模板
登录 钉钉开发者后台。
进入你的应用详情 -> “卡片管理” (或“互动卡片”)。
点击 “创建卡片” -> 选择 “代码编辑” 或 “可视化编辑”。
设计好你的卡片布局(标题、文本、按钮等)。
发布/保存 后,你会获得一个
cardKey(通常格式如sample_card_key_xxxxx或自定义的字符串)。注意:记下这个
cardKey,后面要用。
第二步:修改 API 调用代码
你需要将请求体从“直接写死卡片结构”改为“引用 cardKey + 传入动态数据”。
❌ 错误的请求体 (旧模式,会导致该报错):
{
"msgKey": "sampleCard",
"msgParam": {
"type": "card",
"card": { // <--- 这里直接放卡片结构是错误的
"type": "markdown",
"title": "...",
"content": "..."
}
}
}✅ 正确的请求体 (新模式):
假设你调用的接口是 /v1.0/robot/oToMessages/send (机器人发送) 或 /v1.0/im/agents/messages/send (工作通知)。
{
"msgKey": "sampleCard",
"msgParam": {
"cardKey": "你的_cardKey_在这里", // <--- 核心:填入后台创建的 cardKey
"cardData": { // <--- 核心:只传动态变量
"title": "报警通知",
"content": "服务器 CPU 使用率过高",
"btnText": "查看详情",
"url": "http://example.com"
}
},
"receiverIds": ["用户 ID 或 群聊 ID"]
}cardKey: 必须与后台创建的完全一致。cardData: 这是一个 JSON 对象,里面的字段名必须与你卡片模板中定义的 变量名 (Variable Name) 一一对应。例如模板里写的是${title},这里 key 就是title。
第三步:检查权限与发布状态
卡片是否发布:确保卡片模板在后台状态是 “已发布” (Published),如果是“草稿”状态,API 可能无法读取。
应用权限:
如果是群机器人:确保机器人已添加到群,且卡片模板权限对机器人可见。
如果是工作通知:确保接收用户在应用的可见范围内。
版本匹配:确认你调用的 API 版本支持互动卡片。
推荐接口:
POST /v1.0/im/agents/messages/send(工作通知) 或POST /v1.0/robot/oToMessages/send(群机器人)。
🐍 Python 代码示例 (修正版)
import requests
import json
# 配置信息
ACCESS_TOKEN = "你的 access_token"
CARD_KEY = "sample_card_key_12345" # 【关键】从钉钉后台获取
# 动态数据 (必须与卡片模板中的变量名一致)
card_data = {
"title": "系统告警",
"content": "OpenClaw 检测到异常流量",
"time": "2026-02-26 12:00:00",
"btn_url": "https://www.aliyun.com"
}
# 请求体
payload = {
"msgKey": "sampleCard", # 这里的 msgKey 通常固定为 sampleCard 或你在后台配置的 Key
"msgParam": {
"cardKey": CARD_KEY, # 【关键】填入 cardKey
"cardData": json.dumps(card_data) # 注意:部分接口要求 cardData 是 JSON 字符串,部分是对象,视具体接口文档而定
},
"receiverIds": ["群聊 ID 或 用户 ID"]
}
# 如果接口要求 cardData 是字符串,则上面用 json.dumps()
# 如果接口要求 cardData 是对象,则直接传 card_data 对象
# 钉钉新版接口通常建议 cardData 为对象,但为了兼容,有时需序列化,请查阅具体接口文档
# 修正:大多数 v1.0 接口 msgParam 整体是个对象,cardData 也是对象
payload_correct = {
"msgKey": "sampleCard",
"msgParam": {
"cardKey": CARD_KEY,
"cardData": card_data
},
"receiverIds": ["chatId123"]
}
url = "https://api.dingtalk.com/v1.0/robot/oToMessages/send" # 以机器人为例
headers = {
"x-acs-dingtalk-access-token": ACCESS_TOKEN,
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, data=json.dumps(payload_correct))
print(response.status_code)
print(response.text)💡 快速自查清单
我是否在钉钉后台创建了卡片模板?
我是否复制了正确的
cardKey到代码中?我的
cardData里的字段名是否与模板里的${变量名}完全一致?卡片模板是否已经点击了 “发布”?
我调用的 API 接口是否是 v1.0 新版本接口?(旧版
send接口可能不支持新卡片)
只要补上 cardKey 并确保模板已发布,这个 space of card is empty 错误就会立即消失。