Windows lark-cli.cmd shim mangles multi-line markdown and '>' in --markdown args (routes to user identity / WinError path error)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 17.3k
- Forks
- 1.4k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 105
Description
Summary
On Windows, the lark-cli.cmd shim shipped with @larksuite/cli mangles arguments that contain newlines or >, which breaks the primary agent use-case of im +messages-send --markdown (multi-line, report-style markdown). A multi-line --markdown silently routes the request to the user identity (fails with token_missing even though --as bot was passed), and content with a line-leading > fails with a Windows path error. Both work perfectly when the same command is invoked through node run.js directly, which isolates the bug to the .cmd shim's argument re-passing — the core CLI binary is fine.
Repro
lark-cli 1.0.86, Windows 11, bot identity configured (user identity not logged in).
Case 1 — multi-line --markdown fails via .cmd, works via node run.js:
# via lark-cli.cmd → rc=3, routes to USER identity:
lark-cli.cmd im +messages-send --chat-id oc_xxx --markdown "line one
line two" --as bot
Actual error payload (verbatim):
{
"ok": false,
"identity": "user",
"error": {
"type": "authentication",
"subtype": "token_missing",
"message": "need_user_authorization (user: ou_9aaa070f0debd6a287298322cf962023)",
"hint": "run `lark-cli auth login --scope \"im:message.send_as_user im:message\" --no-wait --json` ...",
"user_open_id": "ou_9aaa070f0debd6a287298322cf962023"
}
}
- Single-line
--markdownvia.cmd→ rc=0,"identity": "bot", sends fine. - Multi-line
--markdownvianode run.js→ rc=0,"identity": "bot", sends fine (so the CLI logic itself handles multi-line correctly).
Case 2 — > inside content fails via .cmd, works via node run.js:
# via lark-cli.cmd → rc=1, WinError "The system cannot find the path specified."
lark-cli.cmd im +messages-send --chat-id oc_xxx \
--content '{"zh_cn":{"content":[[{"tag":"md","text":"# 标题\n\n> 引用行\n\n**正文**"}]]}}' \
--msg-type post --as bot
# via node run.js → rc=0, sends fine.
node node_modules/@larksuite/cli/scripts/run.js im +messages-send --chat-id oc_xxx \
--content '{"zh_cn":{"content":[[{"tag":"md","text":"# 标题\n\n> 引用行\n\n**正文**"}]]}}' \
--msg-type post --as bot
A mid-line > (e.g. l=64.67 > zg=63.30) works fine via .cmd; only a line-leading > (markdown blockquote) triggers the path error.
Why this is worse than a one-shot failure
- AI agents and report push scripts almost always send multi-line markdown (headers, blockquotes, bullet lists). The
.cmdshim is whatnpmexposes as the defaultlark-clientry on Windows (seenode_modules/@larksuite/cli/scripts/run.js+lark-cli.cmd), so every script that doessubprocess.run(["lark-cli", ...])orshutil.which("lark-cli")lands on the broken.cmd. - The failure is silent and misleading: it looks like an auth problem (
need_user_authorization, hint to addim:message.send_as_userscope) when the user is trying to send as a bot that is already fully authorized. This sent us down a false "user auth missing" debugging path before we bisected to the wrapper. - The blockquote case produces a confusing Windows path error for a pure-text payload, so even the manual
--contentworkaround is non-obvious.
Root cause
The shim at node_modules/@larksuite/cli/lark-cli.cmd re-passes argv via %* into a second invocation:
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & set PATHEXT=%PATHEXT:;.JS;=;% & "%_prog%" "%dp0%\node_modules\@larksuite\cli\scripts\run.js" %*
cmd.exe's %* re-expansion loses the original argv quoting: embedded newlines break the argument (the CLI then sees a malformed flag set and falls back to its default user identity), and a line-leading > is treated as output redirection (WinError "The system cannot find the path specified"). The Go binary behind run.js has no such problem — proof is the node run.js control above.
Proposed fixes
(a) Stop re-parsing through %*. The .cmd shim should not reconstruct argv from %*. Preferred: keep the shim as a thin launcher that calls run.js with the exact same argv the shell received (e.g. forward args without re-expansion, or ship a small .exe/node launcher that does spawnSync(node, [runjs, ...process.argv.slice(2)])). This single change resolves both cases.
(b) Fall back loudly instead of silently. Independently of (a), when --as bot is passed but the request would use the user identity, error out with a clear "bot identity requested but user path taken" message rather than returning the confusing token_missing auth hint.
(a) alone is sufficient to unblock affected users.
Workaround (for affected users today)
On Windows, bypass the .cmd shim and call the underlying script with node directly:
node "$(npm root -g)/@larksuite/cli/scripts/run.js" im +messages-send \
--chat-id oc_xxx --markdown "$(cat report.md)" --as bot
For the bundled-runtime case (e.g. inside Hermes): node <node-dir>/node_modules/@larksuite/cli/scripts/run.js ....
Environment
@larksuite/cli1.0.86 (npm), Windows 11 x64- Invocation path:
lark-cli.cmdshim →scripts/run.js→ compiled Go binary (bin/lark-cli.exe) - Identity: bot configured and authorized; user identity intentionally not logged in
- Feishu Open Platform (chat send via
/open-apis/im/v1/messages)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with lark-cli.cmd and compare its %* argument forwarding with scripts/run.js; reproduce both Windows cases using the .cmd shim and the direct node invocation. The fix is done when multiline --markdown and line-leading > content reach the bot identity without authentication or path errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100