Skip to content

fix(node): stop the backend crash on requests without a resolved serv… - #955

Closed
asdf80730 wants to merge 1 commit into
EasyIME:masterfrom
asdf80730:fix/node-pre-init-unknown-service
Closed

asdf80730 wants to merge 1 commit into
EasyIME:masterfrom
asdf80730:fix/node-pre-init-unknown-service

Conversation

@asdf80730

Copy link
Copy Markdown

…ice (#937)

WHAT

  • Return a NULL_SERVICE from the createServer callback when a GUID resolves to no service, instead of null. It answers every method with {success: false, seqNum: n} and passes state through unchanged.
  • Extract the GUID lookup into resolveService() so the callback stays a single call, normalise the requested id once, and log when nothing resolves.
  • Skip the dynamic lookup for an empty id instead of throwing.

WHY
nime stores {service: null} when init names an unknown GUID, and its service = textService default only applies to undefined, not null. The next request therefore dereferences null and kills the backend, dropping every client served by it:

TypeError: Cannot read properties of null (reading 'textReducer')
at handleRequest (nime/src/requestHandler.js:51)

A request whose client id never sent init is handled inside nime before the callback runs, so it stays unprotected; the TSF client always sends init first, which is why this change does not vendor or patch nime.

Generated with assistance from OMP (deepseek-v4.1-flash).

…ice (EasyIME#937)

WHAT
- Return a NULL_SERVICE from the createServer callback when a GUID resolves to
  no service, instead of null. It answers every method with
  {success: false, seqNum: n} and passes state through unchanged.
- Extract the GUID lookup into resolveService() so the callback stays a single
  call, normalise the requested id once, and log when nothing resolves.
- Skip the dynamic lookup for an empty id instead of throwing.

WHY
nime stores {service: null} when init names an unknown GUID, and its
`service = textService` default only applies to undefined, not null. The next
request therefore dereferences null and kills the backend, dropping every client
served by it:

  TypeError: Cannot read properties of null (reading 'textReducer')
      at handleRequest (nime/src/requestHandler.js:51)

A request whose client id never sent init is handled inside nime before the
callback runs, so it stays unprotected; the TSF client always sends init first,
which is why this change does not vendor or patch nime.

Generated with assistance from OMP (deepseek-v4.1-flash).
@PCMan

PCMan commented Oct 18, 2026

Copy link
Copy Markdown
Member

Hi @jessy1092 could you help review if this is correct, please? Thanks!

@ilwu

ilwu commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

用系統 node v24 直接跑 node/server.js,以 stdin/stdout 餵請求(與 PIMELauncher 相同),對照 master (1458cea):

情境 master 本 PR
正常:meow init → onActivate → filterKeyDown OK OK
init 用不存在的 GUID,再送 onActivate TypeError: Cannot set properties of undefined (setting 'isKeyboardOpen'),process 結束 init 回 {"success":false},onActivate 回 {"success":false},process 活著,另一個 client 的 meow init 正常
沒 init 就直接送 onActivate TypeError: Cannot read properties of null (reading 'env'),process 結束 一樣會結束(同樣的 TypeError)

第二個情境是 nime nimeServer.js 在 lineHandler 裡幫新 client 建了 {service: null, state: null},之後 handleClientRequest 直接丟給 handleRequest,這條路不經過 createServer 的 callback,所以本 PR 的 fallback service 碰不到它。正常流程 DLL 一定先 init,但後端被 launcher 重啟時既有 client 的請求就是這個樣子(#909 之後 launcher 會主動斷線讓 client 重新 init,所以機率已經低很多)。若要一起補,在 handleClientRequest 裡遇到 connections[clientId].service == null 時回 {success:false, seqNum} 就可以。

#946 與 #955 修的是同一條路徑,行為在這組測試裡完全一樣。

(測試腳本與本留言由 Claude Code (Claude Fable 5.1) 協助產生)

@asdf80730

Copy link
Copy Markdown
Author

Thanks @ilwu — I reproduced this matrix on node v24 against 1458cea and against your #971 branch:

scenario 1458cea #971
init unknown GUID, then onActivate process exits (TypeError: Cannot set properties of undefined (setting 'isKeyboardOpen')) {"success":false}, then {"success":false,"seqNum":1}; process alive; another client's meow init still succeeds
no init, then onActivate process exits (TypeError: Cannot read properties of null (reading 'env')) {"success":false,"seqNum":0}; process alive
malformed JSON line process exits {"success":false}; process alive

Your diagnosis of the second path is right: nime creates {service: null, state: null} in lineHandler, and handleClientRequest never goes through the createServer callback, so the fallback service in #946/#955 cannot see it.

One more crash while testing: nime splits the line with split('|', 2), so any payload whose JSON contains a | is truncated and JSON.parse throws. On 1458cea, init with "note":"a|b" kills the backend. #971's indexOf('|') change fixes that as well.

Since #946 and this PR fix the same callback path, and #971 covers the remaining nime-internal paths plus the | truncation, I'm closing this as superseded. Thanks for the careful test matrix — I'll put the review effort into #971 instead.

@asdf80730 asdf80730 closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants