Skip to content

Typed SDK response records contradict engine-derived status and channel payloads #754

Description

@MicroMilo

Summary

Backend status and channel services return engine-neutral Status[] and ChannelMessage[] values without remapping, but the typed SDKs declare different fields. StatusRecord omits contact, caption, mediaUrl, backgroundColor, font, and expiresAt while advertising nonexistent statusId/body fields. ChannelRecord substitutes pictureUrl/role for backend picture/verified/inviteCode/createdAt, and the channel-messages endpoint is typed as the unrelated persisted MessageRecord. Java repeats the incompatible channel model.

Impact: SDK users receive runtime objects whose documented type omits real response fields and exposes fields that the server never returns. In particular, JavaScript channel messages are presented as persisted MessageRecord objects with sessionId, chatId, direction, and status even though the endpoint returns ChannelMessage objects containing hasMedia/mediaUrl. This causes incorrect autocomplete, compile-time rejection of valid field access, and successful compilation of accesses that evaluate to undefined.

Code path

Checked against current main at commit 113c4207a08b3f17e5894cef3eda9c80d6abee7d.

  • src/engine/interfaces/whatsapp-engine.interface.ts:230-252
  • src/engine/interfaces/whatsapp-engine.interface.ts:271-289
  • src/modules/status/status.service.ts:9-15
  • src/modules/channel/channel.service.ts:33-34
  • sdk/javascript/src/types.ts:503-515
  • sdk/javascript/src/types.ts:612-634

Steps to reproduce

Validation level: current HEAD supplemental dynamic witness reproduced.

node -e 'const fs=require("fs"); const fields=(p,n)=>{const s=fs.readFileSync(p,"utf8"); const t=s.slice(s.indexOf(`export interface ${n} {`)+`export interface ${n} {`.length); const b=t.slice(0,t.indexOf("\n}")); return [...b.matchAll(/^  ([A-Za-z][A-Za-z0-9]*)\??:/gm)].map(x=>x[1])}; const server="src/engine/interfaces/whatsapp-engine.interface.ts", sdk="sdk/javascript/src/types.ts"; for (const [a,b] of [["Label","LabelRecord"],["Channel","ChannelRecord"],["ChannelMessage","MessageRecord"],["Status","StatusRecord"]]) { const x=fields(server,a), y=fields(sdk,b); console.log(`${a}->${b} server-only=${x.filter(k=>!y.includes(k)).join(",")||"none"} sdk-only=${y.filter(k=>!x.includes(k)).join(",")||"none"}`) }'

Observed output:

Label->LabelRecord server-only=hexColor sdk-only=color,colorHex
Channel->ChannelRecord server-only=inviteCode,picture,verified,createdAt sdk-only=pictureUrl,role
ChannelMessage->MessageRecord server-only=hasMedia,mediaUrl sdk-only=sessionId,waMessageId,chatId,from,to,type,direction,metadata,status,createdAt
Status->StatusRecord server-only=contact,caption,mediaUrl,backgroundColor,font,expiresAt sdk-only=statusId,body

Neighboring control:

node -e 'const fs=require("fs"); const fields=(p,n)=>{const s=fs.readFileSync(p,"utf8"); const t=s.slice(s.indexOf(`export interface ${n} {`)+`export interface ${n} {`.length); const b=t.slice(0,t.indexOf("\n}")); return [...b.matchAll(/^  ([A-Za-z][A-Za-z0-9]*)\??:/gm)].map(x=>x[1])}; const server="src/engine/interfaces/whatsapp-engine.interface.ts", sdk="sdk/javascript/src/types.ts"; for (const [a,b] of [["Catalog","CatalogInfo"],["Product","CatalogProduct"]]) { const x=fields(server,a), y=fields(sdk,b); console.log(`${a}->${b} server-only=${x.filter(k=>!y.includes(k)).join(",")||"none"} sdk-only=${y.filter(k=>!x.includes(k)).join(",")||"none"}`) }'

Control output:

Catalog->CatalogInfo server-only=none sdk-only=none
Product->CatalogProduct server-only=none sdk-only=none

Expected behavior

The documented/source-grounded contract should hold without the drift described above.

Actual behavior

Typed SDK engine-resource models use missing or incorrect fields for direct engine-neutral payloads. B0121 and B0127 overlap on the same label/channel model drift and are resolved by one coordinated wire-model alignment.

Existing coverage

I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.

Suggested fix

Align JavaScript and Java engine-resource response models with the directly returned Contact, Status, Label, Channel, and ChannelMessage wire shapes.

Suggested tests

  • Add a regression test for the reproduction above.
  • Add a neighboring control assertion so the intended non-bug path remains covered.

Submitted with Codex.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions