Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[BUG] Cloudflare KV binding 模式下 kvDriver.list() 沿用 EdgeOne 语义,字段名不匹配导致 key 格式下初始化 500 并耗尽 list 配额

Open Beginner friendly
#125 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

@wumingzhinu is already working on this.

Since Oct 13, 2026.

  • #127 by @wumingzhinu — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
64/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
cloud, typescript
Domain
backend, databases

Research direction

Start in src/backend/internal/model/store/driver/kv.ts around lines 451-482, where the binding-mode list() reads item.key, page.complete and the cursor. Compare it with the Cloudflare semantics in the sibling cfkv.ts (name, list_complete, cursor). Done when the loop terminates on list_complete and returns the real key names. The issue's quota-design question about the key format is separate and should not be bundled into this fix.

Written by the indexing model from the issue text.

Description

bug
请确认以下事项
  • 我确认描述清晰有礼貌,已提供必要诊断信息,在最新版本上可以复现。
  • 我确认已经查过相关文档和已有问题和讨论,本次反馈并非是重复提问。
  • 我确认这是Worker项目问题,并非网络和操作导致,也不是Go版的问题。
  • 我确认已阅读开源许可/免责条款/使用政策,理解使用的风险自行承担。
版本(必填)

main @ d3b79f8 (2026-10-11)

平台(必填)

Cloudflare Workers

DB_FORMAT(必填)

key(分 key)

DB_DRIVER(必填)

kv

存储驱动(必填)

无(问题发生在创建管理员的初始化阶段,尚未挂载任何存储)

问题描述(必填)
现象

在 Cloudflare Workers 上使用 KV binding + DB_FORMAT=key 时,初始化创建管理员必定失败:
/api/public/init/setup 返回 500 Internal Server Error(Hono 未捕获异常)。
仅把 DB_FORMAT 改为 map 后,同一请求返回 200 {"code":200,"message":"success"},初始化成功。

根因

src/backend/internal/model/store/driver/kv.ts 第 451-482 行,binding 模式的 list() 是按 EdgeOne KV 语义实现的(代码注释亦明确写了这点):

const page = await kv.list({ prefix, cursor, limit: 256 })
const pageKeys = Array.isArray(page?.keys) ? page.keys : []
for (const item of pageKeys) {
  if (item?.key) keys.push(item.key)              // ← EdgeOne: item.key
}
if (pageKeys.length > 0) {
  cursor = pageKeys[pageKeys.length - 1].key || ""  // ← 同样是 item.key
}
complete = Boolean(page?.complete) || pageKeys.length === 0  // ← EdgeOne: page.complete

而 Cloudflare Workers KV 的 KVNamespaceListResult 实际返回:

{ keys: { name: string; expiration?: number; metadata?: unknown }[];
  list_complete: boolean; cursor?: string; cacheStatus: string | null }

两处字段名均不匹配:

  1. item.key → CF 为 item.name,因此 keys 恒为空数组,list 事实上失效;
  2. page.complete → CF 为 page.list_complete,恒为 undefined。
后果(两个叠加)
  • 存储为空时:pageKeys.length === 0 使 complete 为 true,循环侥幸退出(但返回空数组,功能已失效)。
  • 存储非空时:complete 恒为 false,且 cursor 被赋为 undefined || "" 即 "",于是每一轮都用相同游标重复拉取同一页,while 一路跑到 guard < 1000 上限 —— 单次 list() 调用就产生 1000 次真实 KV list 请求。

Cloudflare KV 免费版 list 配额为 1000 次/天(read 10 万/天、write 1000/天、delete 1000/天,每日 00:00 UTC 重置)。而 key 格式的 load() / save() 会对 6 张表逐表调用 list(),一次页面访问即可打满当天额度。额度耗尽后 list() 抛错 → saveDb() 抛异常 → Hono 返回裸 500。

附带问题

kvDriver.isAvailable() 只判断 binding 对象是否存在、不做真实读写探测,因此 /api/public/env_check 在存储实际不可写时仍返回「存储就绪」,严重误导排查(本次排查即因此多花大量时间)。

期望
  1. binding 模式改回 Cloudflare 语义(可参考同目录 cfkv.ts 的实现);
  2. 更关键的是配额设计层面:即使字段改对,key 格式「每次 load/save 对 6 张表调 list」在免费版 1000 次/天下仍不可持续(约 167 次访问/天即耗尽)。建议要么在文档中标注 key + kv 不适用于免费版,要么给 key 格式加一个索引键(用一次 get 换取 N 次 list)。
报错信息(必填)
  1. 初始化接口:
POST /api/public/init/setup
→ HTTP 500
→ body: Internal Server Error

(无 code/message JSON,属 Hono 未捕获异常的裸 500)

  1. 环境检查接口(同一时刻,全部"就绪",与存储实际不可写矛盾):
GET /api/public/env_check
→ storage.available=true, error_code=null, db_load_error=null,
  jwt.ready=true, resolved_driver=kv
  1. Cloudflare KV 命名空间控制台提示:
键-值条目无法列出。您在过去 24 小时内超出了计划所含的列出或读取配额。
  1. 切换为 map 后:
POST /api/public/init/setup
→ HTTP 200 {"code":200,"message":"success"}

GET /api/public/init_status
→ initialized:true, ready:true, db_trusted:true, db_load_error:null

说明:未能取到服务端堆栈 —— Workers Observability 需重新部署才会嵌入新版本生效,期间反复触发也拿不到日志。上述结论由源码逐行比对 + 控制台配额提示 + 变量对照实验三者交叉验证得出。

相关配置(必填)
// Cloudflare Dashboard → openlist-worker → 设置 → 运行时变量和密钥(生产)
ENVIRONMENT = production
DB_CIPHER   = none
DB_FORMAT   = key      // ← 出问题的值;改为 map 后正常
DB_DRIVER   = kv
JWT_SECRET  = (密钥/Secret 类型)

// 绑定
KV  → openlist-worker-kv

// wrangler.jsonc(仓库侧,与 Dashboard 一致)
"vars": { "ENVIRONMENT": "production", "DB_CIPHER": "none", "DB_FORMAT": "key", "DB_DRIVER": "kv" }

部署方式:GitHub 连接自动部署(fork 自 OpenListTeam/OpenList-Worker)
账户类型:Cloudflare Workers 免费版

复现链接(可选)

复现站点:https://list.zhuyongfu.cn (当前已切为 map 正常运行,故暂不可在线复现)

最小复现步骤:

  1. Cloudflare Workers 新建 Worker,绑定一个 KV 命名空间,binding 名 KV
  2. 设置变量 DB_DRIVER=kv、DB_FORMAT=key、JWT_SECRET=<任意32字节随机串>
  3. 部署后访问 https://<域名>/@init
  4. 环境检查显示「存储 就绪 / JWT 密钥 就绪」,点「继续」,填写管理员用户名与密码后提交
  5. POST /api/public/init/setup 返回 500

最小代码级复现(在任意 CF Worker 中执行):

const page = await env.KV.list({ prefix: "user:", limit: 256 })
console.log(page.list_complete, page.keys[0]?.name)  // true, "user:1"
console.log(page.complete, page.keys[0]?.key)        // undefined, undefined  ← kv.ts 正在读的就是这两个

参考:

Dominant language
TypeScript
Stars
573
Forks
926
Avg merge
3d 1h
Merged PRs (30d)
27

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from OpenListTeam/OpenList-Worker

All issues in OpenListTeam/OpenList-Worker

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.