Skip to content

Windows lark-cli.cmd shim mangles multi-line markdown and '>' in --markdown args (routes to user identity / WinError path error) #2507

Description

@ranlingfeng

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 --markdown via .cmd → rc=0, "identity": "bot", sends fine.
  • Multi-line --markdown via node 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

  1. AI agents and report push scripts almost always send multi-line markdown (headers, blockquotes, bullet lists). The .cmd shim is what npm exposes as the default lark-cli entry on Windows (see node_modules/@larksuite/cli/scripts/run.js + lark-cli.cmd), so every script that does subprocess.run(["lark-cli", ...]) or shutil.which("lark-cli") lands on the broken .cmd.
  2. The failure is silent and misleading: it looks like an auth problem (need_user_authorization, hint to add im:message.send_as_user scope) 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.
  3. The blockquote case produces a confusing Windows path error for a pure-text payload, so even the manual --content workaround 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/cli 1.0.86 (npm), Windows 11 x64
  • Invocation path: lark-cli.cmd shim → 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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain/imPR touches the im domain

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions