Skip to content

macOS: cookie login hangs forever — getpass exceeds 1024-byte MAX_CANON tty limit #82

Description

@ychani

Summary

On macOS, option 1 (browser session cookies) in login_setup.py can never succeed. The script hangs forever at the Paste the Cookie header value: prompt, and Enter does nothing.

The cause is a hard tty limit, not a bad cookie or a slow network call.

Cause

login_setup.py:42 reads the cookie with getpass.getpass():

cookie_string = getpass.getpass("Paste the Cookie header value: ").strip()

getpass reads a line from /dev/tty in canonical mode. Canonical-mode input is capped at MAX_CANON bytes per line, and on Darwin that is 1024. A real Monarch Cookie header is several KB (Segment ajs_*, Osano consent, Cloudflare, plus the auth cookies), so the line never terminates: the terminal emits BEL for every byte past the limit and discards it. readline() blocks indefinitely.

Linux sets MAX_CANON to 4096, which is probably why this hasn't shown up in testing.

Reproduction

This reproduces the line-discipline behavior directly, no Monarch account needed (macOS 26.5.2, CPython 3.13.14):

import os, pty, termios, time

def trial(n):
    pid, fd = pty.fork()
    if pid == 0:
        f = open('/dev/tty', 'r')
        attrs = termios.tcgetattr(f)
        attrs[3] = attrs[3] & ~termios.ECHO   # what getpass does; ICANON stays on
        termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
        line = f.readline()
        os.write(1, b"GOT:%d\n" % len(line))
        os._exit(0)
    time.sleep(0.4)
    os.write(fd, b"C" * n + b"\n")
    time.sleep(0.6)
    os.set_blocking(fd, False)
    try:
        out = os.read(fd, 65536)
    except Exception as e:
        out = repr(e).encode()
    os.kill(pid, 9); os.waitpid(pid, 0)
    print(f"sent {n:5d} bytes -> {out[:40]!r}")

for n in (900, 1023, 1030, 4000):
    trial(n)

Output:

sent   900 bytes -> b'GOT:901\r\n'
sent  1023 bytes -> b'GOT:1024\r\n'
sent  1030 bytes -> b'\x07\x07\x07\x07\x07\x07\x07'
sent  4000 bytes -> b'\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07...'

1023 bytes works, 1030 does not. Past the limit the read never completes, which matches the hang. Interrupting produces:

File "login_setup.py", line 42, in _login_with_cookies
    cookie_string = getpass.getpass("Paste the Cookie header value: ").strip()
...
File ".../getpass.py", line 146, in _raw_input
    line = input.readline()
KeyboardInterrupt

This is also user-hostile to diagnose, because getpass echoes nothing — there is no feedback distinguishing "waiting for input" from "input silently dropped."

Suggested fixes

Any of these avoids the canonical-mode limit:

  1. Read the clipboard. The user has just copied the value anyway. subprocess.run(["pbpaste"], ...) on macOS, xclip -o/wl-paste on Linux, Get-Clipboard on Windows. Best UX by far: no prompt, no paste into a tty at all.
  2. Read from a file. Prompt for a path, or accept --cookie-file. Also keeps the secret out of shell history.
  3. Read from an env var, e.g. MONARCH_COOKIE.
  4. Turn off ICANON for the read and accumulate until newline, instead of relying on the line discipline.

Option 4 keeps the existing UX but is the fiddliest; option 1 or 2 is probably the smallest safe change. A fallback chain (try the tty read, and if it returns empty or the process is on Darwin, fall back to clipboard/file) would preserve current behavior on Linux.

It would also help to validate the parsed result and tell the user what was actually captured — printing cookie names and byte count (never values) makes "I grabbed the wrong request in DevTools" immediately obvious, since the analytics-only cookies from a non-api.monarch.com request are a separate easy mistake.

I worked around it locally with a small clipboard-based script and authenticated successfully on the first try, so the cookie auth path itself works fine — it is only the input method that is broken. Happy to send a PR if you have a preference among the above.

Environment

  • macOS 26.5.2 (Darwin 25.5.0), arm64
  • Python 3.13.14
  • monarchmoneycommunity 1.4.0
  • monarch-mcp-server @ ca6c159 (main, cloned 2026-07-26)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions