Skip to content

Device-flow auth login guidance makes AI agents block the turn, causing silent hangs on non-streaming harnesses #928

Description

@areslp

Summary

lark-cli's device-flow authorization guidance for AI agents steers the agent into running the blocking auth login --device-code poll inside the same agent turn that produced the verification URL. On agent harnesses that deliver only the agent's final turn message (i.e. they don't stream intermediate output), the verification URL is never delivered — it stays buffered in a turn that never completes while the poll blocks. The user sees nothing and the agent appears to hang silently, usually until the device code expires, after which it often retries, producing an effectively silent infinite loop.

Environment

  • lark-cli (@larksuite/cli) 1.0.30
  • Harness: a headless claude agent (Claude Code -p / print mode) embedded in a chat gateway that posts the agent's reply to chat only when the turn completes; intermediate assistant messages are not streamed.

What happens

  1. lark-cli docs +fetch …Error: need_user_authorization (user: ou_…)
  2. lark-cli auth login --domain docs --no-wait --json → returns device_code, verification_url, and a hint.
  3. The hint says, verbatim: "Show verification_url to the user … Then immediately execute: lark-cli auth login --device-code <code> (blocks until authorized or timeout). Do not instruct the user to run this command themselves." — and auth login --help says "this command blocks until the user completes authorization in the browser. Run it in the background…".
  4. Following that guidance, the agent emits the URL as an assistant message and then runs the blocking --device-code poll within the same turn.
  5. The turn never completes (the poll blocks for the device code's full lifetime). Because the harness delivers only the final turn message, the URL the agent emitted is never sent to the user.
  6. The user sees nothing; the device code expires; the agent commonly retries from step 2.

Observed in practice: the agent produced a correct "please authorize: <url>" message six times over ~30 minutes — the user received none of them, because the turn never ended.

Why the current guidance is fragile

It implicitly assumes the harness surfaces output produced mid-turn (streaming). Interactive Claude Code in a terminal does. But many chat-gateway / async agent integrations deliver only the agent's final turn output — for those, "show the URL, then block in-turn" guarantees the URL is never seen.

Suggested fix

Change the device-flow guidance — the auth login --help "For AI agents" text, the hint field in auth login --no-wait --json output, and the bundled lark-* agent skills — to recommend a yield pattern instead of a block pattern:

On need_user_authorization: run auth login --no-wait to obtain the verification URL, present the URL to the user as the final message of the turn, then end the turn / return control. Do not block on --device-code within the same turn. Complete authorization by polling --device-code in a separate, later step (e.g. after the user replies that they have authorized).

The --no-wait / --device-code split already supports this cleanly — only the guidance steers agents the wrong way. At minimum, the hint should drop "immediately execute … (blocks …)" and make clear that, for harnesses without mid-turn streaming, the agent should end its turn after presenting the URL.


(Likely distinct from #747, which concerns the poll's timeout duration rather than the turn-blocking guidance.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain/authAuthentication subsystemdomain/coreCLI framework and core librariesdomain/docDocs domain

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions