fix: use AppStateSyncKeyData.fromObject instead of .create for app-state keys#2593
fix: use AppStateSyncKeyData.fromObject instead of .create for app-state keys#2593ropic wants to merge 1 commit into
Conversation
…ate keys When reading app-state-sync-key data from any auth-state provider (Prisma, Redis, files), the stored JSON is deserialized with BufferJSON.reviver which correctly restores Buffer objects. However, wrapping the result with AppStateSyncKeyData.create() does not coerce the restored Buffer fields to the Uint8Array types that the proto library expects for all downstream operations. This produces incorrect keyData in the HKDF derivation step, which in turn generates a wrong AES-256-CBC key and causes "error:1C800064:Provider routines::bad decrypt" on every attempt to decode app-state mutations (labels, archives, mutes, etc.). AppStateSyncKeyData.fromObject() performs full type conversion of all fields from a plain JS object, which is exactly what is needed after JSON deserialization. Affects all three auth-state providers: Prisma, Redis, and file-based. Tested with the Prisma provider — no app-state events fired before this fix; all events (labels.association, chats.update, etc.) fire correctly after.
Reviewer's guide (collapsed on small PRs)Reviewer's GuideReplaces protobuf .create() with .fromObject() when reading app-state-sync-key values across all three auth-state providers so that JSON-deserialized key objects are correctly converted into proto types, fixing app-state decryption and event emission. Sequence diagram for reading app-state-sync-key with fromObject instead of createsequenceDiagram
participant AuthStateProvider
participant Storage
participant BufferJSON
participant AppStateSyncKeyData
AuthStateProvider->>Storage: readData(type_id)
Storage-->>AuthStateProvider: value_json
AuthStateProvider->>BufferJSON: reviver(value_json)
BufferJSON-->>AuthStateProvider: value_object
alt using_create
AuthStateProvider->>AppStateSyncKeyData: create(value_object)
AppStateSyncKeyData-->>AuthStateProvider: keyData_invalid
Note over AuthStateProvider: [bad decrypt, no app-state events]
else using_fromObject
AuthStateProvider->>AppStateSyncKeyData: fromObject(value_object)
AppStateSyncKeyData-->>AuthStateProvider: keyData_valid
Note over AuthStateProvider: [decryption ok, events emitted]
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- Since the
app-state-sync-keyhandling logic is now identical across the three auth-state providers, consider extracting this into a shared helper (e.g.,normalizeAppStateSyncKeyData) to avoid duplication and keep future changes to this behavior centralized.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Since the `app-state-sync-key` handling logic is now identical across the three auth-state providers, consider extracting this into a shared helper (e.g., `normalizeAppStateSyncKeyData`) to avoid duplication and keep future changes to this behavior centralized.
## Individual Comments
### Comment 1
<location path="src/utils/use-multi-file-auth-state-prisma.ts" line_range="185" />
<code_context>
let value = await readData(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) {
- value = proto.Message.AppStateSyncKeyData.create(value);
+ value = proto.Message.AppStateSyncKeyData.fromObject(value);
}
</code_context>
<issue_to_address>
**suggestion:** Consider extracting the AppStateSyncKey deserialization into a shared helper to avoid duplication across providers.
This `AppStateSyncKeyData.fromObject` logic is now duplicated in the Prisma, file-based, and Redis implementations. Pulling it into a shared helper (e.g., `deserializeAppStateSyncKey(type, value)`) would keep behavior consistent if the proto or conversion changes and simplify handling future edge cases like versioned payloads across all providers.
Suggested implementation:
```typescript
let value = await readData(`${type}-${id}`);
value = deserializeAppStateSyncKey(type, value);
data[id] = value;
```
To fully implement the suggestion, you should also:
1. Add an import for the shared helper at the top of this file:
- `import { deserializeAppStateSyncKey } from './app-state-sync-key';`
(adjust the relative path to match your existing utils structure, e.g. `../utils/app-state-sync-key` if appropriate).
2. Create a shared helper module (e.g. `src/utils/app-state-sync-key.ts`) with behavior like:
```ts
import { proto } from '@adiwajshing/baileys'; // or wherever proto comes from
export function deserializeAppStateSyncKey(type: string, value: any) {
if (type === 'app-state-sync-key' && value) {
return proto.Message.AppStateSyncKeyData.fromObject(value);
}
return value;
}
```
3. Update the file-based and Redis implementations to also use this helper instead of inlining:
```ts
value = deserializeAppStateSyncKey(type, value);
```
at the corresponding locations where `proto.Message.AppStateSyncKeyData.fromObject(value)` is currently used.
These changes will centralize the AppStateSyncKey deserialization logic and keep behavior consistent across all providers.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| let value = await readData(`${type}-${id}`); | ||
| if (type === 'app-state-sync-key' && value) { | ||
| value = proto.Message.AppStateSyncKeyData.create(value); | ||
| value = proto.Message.AppStateSyncKeyData.fromObject(value); |
There was a problem hiding this comment.
suggestion: Consider extracting the AppStateSyncKey deserialization into a shared helper to avoid duplication across providers.
This AppStateSyncKeyData.fromObject logic is now duplicated in the Prisma, file-based, and Redis implementations. Pulling it into a shared helper (e.g., deserializeAppStateSyncKey(type, value)) would keep behavior consistent if the proto or conversion changes and simplify handling future edge cases like versioned payloads across all providers.
Suggested implementation:
let value = await readData(`${type}-${id}`);
value = deserializeAppStateSyncKey(type, value);
data[id] = value;To fully implement the suggestion, you should also:
-
Add an import for the shared helper at the top of this file:
import { deserializeAppStateSyncKey } from './app-state-sync-key';
(adjust the relative path to match your existing utils structure, e.g.../utils/app-state-sync-keyif appropriate).
-
Create a shared helper module (e.g.
src/utils/app-state-sync-key.ts) with behavior like:import { proto } from '@adiwajshing/baileys'; // or wherever proto comes from export function deserializeAppStateSyncKey(type: string, value: any) { if (type === 'app-state-sync-key' && value) { return proto.Message.AppStateSyncKeyData.fromObject(value); } return value; }
-
Update the file-based and Redis implementations to also use this helper instead of inlining:
value = deserializeAppStateSyncKey(type, value);
at the corresponding locations where
proto.Message.AppStateSyncKeyData.fromObject(value)is currently used.
These changes will centralize the AppStateSyncKey deserialization logic and keep behavior consistent across all providers.
Problem
All three auth-state providers (Prisma, Redis, file-based) read
app-state-sync-keydata using:This causes
error:1C800064:Provider routines::bad decrypton every attempt to decode app-state mutations. As a result, no app-state events fire (labels.association,chats.updatefrom archives, mutes, etc.).Root Cause
The write path stores keys with
JSON.stringify(value, BufferJSON.replacer)— Baileys' serializer that encodesBuffer/Uint8Arrayfields as{"type":"Buffer","data":"<base64>"}.The read path parses them with
JSON.parse(stored, BufferJSON.reviver)— which correctly reconstructsBufferobjects.However, after
reviverruns, the code wraps the result with.create(value). The protobuf.create()method performs no type conversion — it copies fields as-is. It does not coerce a restoredBufferto the exactUint8Arraylayout that the proto field expects for all downstream operations. This produces akeyDatabuffer that is wrong for HKDF key derivation, leading to a wrong AES-256-CBC key and thebad decrypterror..fromObject(value), on the other hand, is specifically designed to accept a plain JS object and convert all fields to their correct proto types — which is exactly what we need after JSON deserialization.Fix
In all three auth-state files, change
.createto.fromObject:if (type === 'app-state-sync-key' && value) { - value = proto.Message.AppStateSyncKeyData.create(value); + value = proto.Message.AppStateSyncKeyData.fromObject(value); }Files changed:
src/utils/use-multi-file-auth-state-prisma.tssrc/utils/use-multi-file-auth-state-provider-files.tssrc/utils/use-multi-file-auth-state-redis-db.tsVerified Behavior
After this fix:
bad decrypt)regularcollection on first connect)labels.association,chats.update, and other app-state events fire correctlyTested with Evolution API v2.3.7, Baileys 7.0.0-rc.9, PostgreSQL auth-state provider.
The fix is identical and necessary for all three providers.
Summary by Sourcery
Bug Fixes: