Skip to content

[Bug] OpenSpace interactive mode incompatible with launchd — EOFError loop, 100% CPU, 100GB+ logs #87

Description

@shao-zhijie

Bug Description

When OpenSpace CLI is run as a launchd service (macOS background daemon), it enters an infinite EOFError loop because the interactive-mode input() call has no TTY. This causes:

  1. Massive log growth: 100GB+ of EOFError messages within hours
  2. 100% CPU usage: Single Python process spins endlessly on input()
  3. launchd restart loop: Service keeps respawning and failing

Environment

  • macOS (Apple Silicon, ARM64)
  • OpenSpace installed via Homebrew: brew install hkusd/opspace/opspace
  • Launched via launchd plist with KeepAlive: true

Root Cause

The openspace CLI defaults to interactive mode when run without arguments:

# openspace/__main__.py (simplified)
query = input(f"\n{prompt}").strip()  # blocks waiting for stdin

When launched by launchd, there is no TTY/stdin — input() immediately returns EOFError. The error is caught and logged, then the process exits. Because KeepAlive: true, launchd restarts it immediately, creating an infinite restart loop.

Reproduction

Minimal reproduction (no launchd needed)

# This simulates what launchd does — run openspace with no stdin
printf '' | /opt/homebrew/bin/openspace

Expected: Process should fail fast with a clear error message
Actual: Infinite EOFError loop with massive log output

Full launchd reproduction

  1. Create a plist at ~/Library/LaunchAgents/com.example.openspace.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.openspace</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/openspace</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>
  1. Load it:
launchctl bootstrap gui/501 ~/Library/LaunchAgents/com.example.openspace.plist
  1. Watch the log grow:
tail -f ~/Library/Logs/OpenSpace/*.log  # or wherever logs are written

Suggested Fixes

Option 1: Detect non-TTY and exit fast with a clear error

In openspace/__main__.py, check if stdin is a TTY at startup:

import sys
import os

if not sys.stdin.isatty():
    print("ERROR: openspace interactive mode requires a terminal."
          "For background/service use, run: openspace communication run",
          file=sys.stderr)
    sys.exit(1)

Option 2: Add explicit daemon/service mode

If openspace communication run is the intended production mode, add a prominent note in README and --help output.

Additional Issue: Invalid FEISHU_GROUP_POLICY value

When running openspace communication run, an invalid environment variable value causes a validation error:

  • FEISHU_GROUP_POLICY=open is set in the environment
  • The config schema only allows: 'disabled' | 'mention_only' | 'reply_or_mention' | 'all'
  • This causes pydantic ValidationError on startup

Quick fix: Set FEISHU_GROUP_POLICY=all (or any valid value) in the launchd environment.

Impact

  • For macOS users wanting to run OpenSpace as a background service (common for MCP integration), the current CLI is unusable with launchd.
  • The massive log growth can fill up disk space quickly.
  • The KeepAlive restart loop means the service never stabilizes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions