Skip to content

fix(openclaw): saveMemories posts PersonalAddRequest envelope with top-level user_id (closes #237)#244

Open
Fearvox wants to merge 1 commit into
EverMind-AI:mainfrom
Fearvox:fix/issue-237-personaladd-envelope
Open

fix(openclaw): saveMemories posts PersonalAddRequest envelope with top-level user_id (closes #237)#244
Fearvox wants to merge 1 commit into
EverMind-AI:mainfrom
Fearvox:fix/issue-237-personaladd-envelope

Conversation

@Fearvox
Copy link
Copy Markdown
Collaborator

@Fearvox Fearvox commented Jun 3, 2026

What

POST /api/v1/memories returned HTTP 422 "Field required: user_id" because
saveMemories sent flat, per-message bodies with no top-level user_id. The v1
PersonalAddRequest DTO is an envelope: a required top-level user_id plus
a messages[] list of MessageItem, and each MessageItem requires an integer
unix-milliseconds timestamp. This is the exact defect in #237.

This PR rewrites saveMemories to send a single batched envelope and align each
item with the DTO:

{ user_id, session_id?, messages: [ { message_id, timestamp, role, sender_name, content, sender_id? } ] }
  • timestamp: required unix-ms integer. The old create_time ISO string was
    ignored by the converter, leaving timestamp missing → 422.
  • sender_id: set to user_id only for role=user; omitted for
    role=assistant so the backend derives a distinct id (the personal-scene
    converter rejects assistant turns whose sender_id equals user_id).
  • session_id: plumbed through from the OpenClaw sessionKey (engine.js)
    for conversation isolation; optional in the DTO.

Why

Without a top-level user_id the request never passes validation, so the plugin
silently persisted nothing — every turn 422'd. Posting one batched envelope
also replaces the previous one-request-per-message loop.

Verification

Live against a running EverCore (:1995):

body result
old flat per-message body HTTP 422 {"message":"Field required: user_id"}
new envelope (this PR) HTTP 200 {"status":"accumulated","message":"Messages accepted"}

The real saveMemories() from this branch was also driven against the live
backend end-to-end and the POST was accepted (2xx).

Offline regression — adds test/save-memories.test.js (node --test, no
live stack): asserts a single batched POST to /api/v1/memories whose body
carries a top-level user_id, a session_id, a messages array with integer
timestamps and no legacy create_time, and the correct per-role sender_id
semantics; plus that an empty batch sends nothing.

cd methods/EverCore/examples/openclaw-plugin
node --test test/save-memories.test.js

Result: 2 passed.

Scope

Surgical: src/api.js (the envelope rewrite) + the src/engine.js one-line
sessionId plumbing + its regression test. Deliberately does not bundle the
separate api.pluginConfig forwarding fix (#150), which ships as its own PR.

Closes #237.

Credit

Prior art adopted:

Co-authored-by: kevinwon 2725361+ww-k@users.noreply.github.com
Co-authored-by: Void Freud 246163318+voidfreud@users.noreply.github.com
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

🤖 Generated with Claude Code

…top-level user_id (closes EverMind-AI#237)

POST /api/v1/memories returned HTTP 422 ("Field required: user_id") because
saveMemories sent flat, per-message bodies with no top-level user_id. The v1
PersonalAddRequest DTO is an envelope: a required top-level user_id plus a
messages[] list of MessageItem, and each MessageItem requires an integer
unix-milliseconds timestamp.

Rewrite saveMemories to send a single batched envelope { user_id, session_id?,
messages: [...] } and align each item with the MessageItem DTO:
- timestamp: required unix-ms integer (the old create_time ISO string was
  ignored by the converter, leaving timestamp missing -> 422).
- sender_id: set to user_id only for role=user; omitted for role=assistant so
  the backend derives a distinct id (personal-scene converter rejects assistant
  turns whose sender_id equals user_id).
- session_id: plumbed through from the OpenClaw sessionKey (engine.js) for
  conversation isolation; optional in the DTO.

Verified live against a running EverCore on :1995: the old flat body returns
422 "Field required: user_id"; the new envelope returns 200
{"status":"accumulated","message":"Messages accepted"}.

Adds test/save-memories.test.js (node --test, offline) asserting the POST body
carries a top-level user_id, a session_id, a messages array with integer
timestamps and no legacy create_time, and the correct per-role sender_id
semantics; plus that an empty batch sends nothing.

Scope: api.js + the engine.js sessionId plumbing + its regression test. Does not
bundle the separate api.pluginConfig forwarding fix (EverMind-AI#150/EverMind-AI#139), which ships as
its own PR.

Prior art adopted: PR EverMind-AI#189 (kevinwon) converted saveMemories to the same
top-level-user_id batch envelope; PR EverMind-AI#128 (Void Freud) set user_id at the top
level in the same saveMemories path.

Closes EverMind-AI#237.

Co-authored-by: kevinwon <2725361+ww-k@users.noreply.github.com>
Co-authored-by: Void Freud <246163318+voidfreud@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 3, 2026 05:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

[bug] OpenClaw plugin saveMemories: POST /api/v1/memories 422 (Field required: user_id)

2 participants