Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Send() blocks indefinitely when ReadyForInitialPrompt returns false despite Status() reporting "stable" #209

Copy link
Copy link
@johnstcn

Description

@johnstcn
Issue body actions

Bug

POST /message with type: "user" hangs indefinitely when ReadyForInitialPrompt never returns true for the current screen, even though GET /status returns "stable".

Root cause

statusLocked() does not check initialPromptReady. It returns "stable" when the screen is stable and the queue is empty. Send() passes the status validation and enqueues the message. But the stableSignal (which the send loop waits on) requires initialPromptReady to be true. If ReadyForInitialPrompt never returns true, the signal never fires and Send() blocks forever.

statusLocked() = "stable"   →  Send() passes status check, enqueues
stableSignal requires:       →  initialPromptReady && queue > 0 && screenStable
initialPromptReady = false   →  signal never fires
Send() blocks on errCh       →  forever

Real-world trigger

Claude Code v2.1.87 shows a theme selection onboarding screen that uses ╌╌╌ (Unicode dashed lines) instead of ─── (box-drawing characters). The message box detection in findGreaterThanMessageBox / findGenericSlimMessageBox fails because it looks for ─────────────── and > which aren't present on this screen. initialPromptReady stays false and all user-type messages hang.

Last 6 lines of the actual screen content:

  1  function greet() {                                                         
  2 -  console.log("Hello, World!");                                            
  2 +  console.log("Hello, Claude!");                                           
  3  }                                                                          
 ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ 
  Syntax theme: Monokai Extended (ctrl+t to disable)                            

Note: (U+254C, BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL) vs (U+2500, BOX DRAWINGS LIGHT HORIZONTAL).

Reproducing test

func TestSendBlocksWhenInitialPromptNotReady(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	t.Cleanup(cancel)

	mClock := quartz.NewMock(t)
	agent := &testAgent{screen: "onboarding screen without message box"}
	cfg := st.PTYConversationConfig{
		Clock:                 mClock,
		SnapshotInterval:      100 * time.Millisecond,
		ScreenStabilityLength: 200 * time.Millisecond,
		AgentIO:               agent,
		ReadyForInitialPrompt: func(screen string) bool {
			return false // Simulates failed message box detection.
		},
		Logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
	}
	c := st.NewPTY(ctx, cfg, &testEmitter{})
	c.Start(ctx)

	// Fill snapshot buffer to reach stability.
	advanceFor(ctx, t, mClock, 300*time.Millisecond)

	// Status reports "stable" — Send() will pass the status check.
	assert.Equal(t, st.ConversationStatusStable, c.Status())

	// But Send() blocks forever because stableSignal requires
	// initialPromptReady, which is false.
	var sendDone atomic.Bool
	go func() {
		_ = c.Send(st.MessagePartText{Content: "hello"})
		sendDone.Store(true)
	}()
	advanceFor(ctx, t, mClock, 20*time.Second)

	assert.False(t, sendDone.Load(),
		"Send() blocks forever: status said stable but stableSignal never fires")
}

Possible fixes

  1. Make statusLocked() check initialPromptReady: return "initializing" or "changing" when initialPromptReady is false. This would cause Send() to reject the message with ErrMessageValidationChanging instead of hanging.
  2. Improve message box detection: add ╌╌╌ as an alternative to ─── in findGreaterThanMessageBox / findGenericSlimMessageBox.
  3. Both: fix the detection AND add the safety check in statusLocked().

Option 3 is likely the right approach — fix the detection for this specific Claude Code version, and add the statusLocked guard so future detection failures fail fast instead of hanging.

Discovered while smoke-testing #208.

🤖 This response was generated by Coder Agents.

Reactions are currently unavailable

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.