Skip to content

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

@shao-zhijie

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions