Title: Send() blocks indefinitely when ReadyForInitialPrompt returns false despite Status() reporting "stable" · Issue #209 · coder/agentapi · GitHub
Open Graph Title: Send() blocks indefinitely when ReadyForInitialPrompt returns false despite Status() reporting "stable" · Issue #209 · coder/agentapi
X Title: Send() blocks indefinitely when ReadyForInitialPrompt returns false despite Status() reporting "stable" · Issue #209 · coder/agentapi
Description: 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 return...
Open Graph Description: 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...
X Description: 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...
Opengraph URL: https://github.com/coder/agentapi/issues/209
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Send() blocks indefinitely when ReadyForInitialPrompt returns false despite Status() reporting \"stable\"","articleBody":"## Bug\n\n`POST /message` with `type: \"user\"` hangs indefinitely when `ReadyForInitialPrompt` never returns `true` for the current screen, even though `GET /status` returns `\"stable\"`.\n\n## Root cause\n\n`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.\n\n```\nstatusLocked() = \"stable\" → Send() passes status check, enqueues\nstableSignal requires: → initialPromptReady \u0026\u0026 queue \u003e 0 \u0026\u0026 screenStable\ninitialPromptReady = false → signal never fires\nSend() blocks on errCh → forever\n```\n\n## Real-world trigger\n\nClaude 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 `\u003e` which aren't present on this screen. `initialPromptReady` stays `false` and all `user`-type messages hang.\n\nLast 6 lines of the actual screen content:\n```\n 1 function greet() { \n 2 - console.log(\"Hello, World!\"); \n 2 + console.log(\"Hello, Claude!\"); \n 3 } \n ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ \n Syntax theme: Monokai Extended (ctrl+t to disable) \n```\n\nNote: `╌` (U+254C, BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL) vs `─` (U+2500, BOX DRAWINGS LIGHT HORIZONTAL).\n\n## Reproducing test\n\n```go\nfunc TestSendBlocksWhenInitialPromptNotReady(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tt.Cleanup(cancel)\n\n\tmClock := quartz.NewMock(t)\n\tagent := \u0026testAgent{screen: \"onboarding screen without message box\"}\n\tcfg := st.PTYConversationConfig{\n\t\tClock: mClock,\n\t\tSnapshotInterval: 100 * time.Millisecond,\n\t\tScreenStabilityLength: 200 * time.Millisecond,\n\t\tAgentIO: agent,\n\t\tReadyForInitialPrompt: func(screen string) bool {\n\t\t\treturn false // Simulates failed message box detection.\n\t\t},\n\t\tLogger: slog.New(slog.NewTextHandler(io.Discard, nil)),\n\t}\n\tc := st.NewPTY(ctx, cfg, \u0026testEmitter{})\n\tc.Start(ctx)\n\n\t// Fill snapshot buffer to reach stability.\n\tadvanceFor(ctx, t, mClock, 300*time.Millisecond)\n\n\t// Status reports \"stable\" — Send() will pass the status check.\n\tassert.Equal(t, st.ConversationStatusStable, c.Status())\n\n\t// But Send() blocks forever because stableSignal requires\n\t// initialPromptReady, which is false.\n\tvar sendDone atomic.Bool\n\tgo func() {\n\t\t_ = c.Send(st.MessagePartText{Content: \"hello\"})\n\t\tsendDone.Store(true)\n\t}()\n\tadvanceFor(ctx, t, mClock, 20*time.Second)\n\n\tassert.False(t, sendDone.Load(),\n\t\t\"Send() blocks forever: status said stable but stableSignal never fires\")\n}\n```\n\n## Possible fixes\n\n1. **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.\n2. **Improve message box detection**: add `╌╌╌` as an alternative to `───` in `findGreaterThanMessageBox` / `findGenericSlimMessageBox`.\n3. **Both**: fix the detection AND add the safety check in `statusLocked()`.\n\nOption 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.\n\nDiscovered while smoke-testing #208.\n\n\u003e 🤖 This response was generated by Coder Agents.","author":{"url":"https://github.com/johnstcn","@type":"Person","name":"johnstcn"},"datePublished":"2026-03-31T13:36:06.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/209/agentapi/issues/209"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:ee275574-d823-9ff9-2673-2cecbb4e6d29 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | CD2A:11597D:1CCA8D:26380F:6A4F281B |
| html-safe-nonce | c8c744b36419c8b2eccb2a9a19de31b369f8dacd3590deec1007157e83f34032 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDRDJBOjExNTk3RDoxQ0NBOEQ6MjYzODBGOjZBNEYyODFCIiwidmlzaXRvcl9pZCI6Ijg4Mzg2OTczMTI4NjE0OTczNzEiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 206bfa3ec7cd38a5b3c9ebb888c696d142e19dfdf06eb46889b513378006a93b |
| hovercard-subject-tag | issue:4179279684 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/coder/agentapi/209/issue_layout |
| twitter:image | https://opengraph.githubassets.com/e5cb23052dea94cb9d703d7c38456c2fdc03cb190e9f369efbf1bf98f10c0fdb/coder/agentapi/issues/209 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/e5cb23052dea94cb9d703d7c38456c2fdc03cb190e9f369efbf1bf98f10c0fdb/coder/agentapi/issues/209 |
| og:image:alt | 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... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | johnstcn |
| hostname | github.com |
| expected-hostname | github.com |
| None | b92d11c0aa4a77d54ef4af1078b6a15fb5a70a215b30c4ecf28889d5a8e656d9 |
| turbo-cache-control | no-preview |
| go-import | github.com/coder/agentapi git https://github.com/coder/agentapi.git |
| octolytics-dimension-user_id | 95932066 |
| octolytics-dimension-user_login | coder |
| octolytics-dimension-repository_id | 961832048 |
| octolytics-dimension-repository_nwo | coder/agentapi |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 961832048 |
| octolytics-dimension-repository_network_root_nwo | coder/agentapi |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 2b8f23afb982271f1b22258a94aede67a6b77760 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width