Skip to content

fix: use AppStateSyncKeyData.fromObject instead of .create for app-state keys#2610

Open
ropic wants to merge 1 commit into
evolution-foundation:developfrom
ropic:fix/app-state-sync-key-fromobject-v2
Open

fix: use AppStateSyncKeyData.fromObject instead of .create for app-state keys#2610
ropic wants to merge 1 commit into
evolution-foundation:developfrom
ropic:fix/app-state-sync-key-fromobject-v2

Conversation

@ropic

@ropic ropic commented Jun 26, 2026

Copy link
Copy Markdown

Problem

All three auth-state providers (Prisma, Redis, files) read app-state-sync-key data using:

value = proto.Message.AppStateSyncKeyData.create(value);

This causes error:1C800064:Provider routines::bad decrypt whenever Baileys tries to decode app-state mutations (labels, archives, mutes, etc.). The result is that no app-state events ever fire.

Root cause

The write path serializes keys with JSON.stringify(value, BufferJSON.replacer), encoding Buffer/Uint8Array fields as {"type":"Buffer","data":"<base64>"}. The read path restores them via JSON.parse(stored, BufferJSON.reviver), which correctly reconstructs Buffer objects.

However, .create(value) performs no type conversion — it copies fields as-is and does not coerce a restored Buffer back to the Uint8Array the proto field expects. This produces a structurally wrong keyData in the HKDF derivation step, leading to a wrong AES-256-CBC key and the bad decrypt error.

.fromObject(value) is designed to accept a plain JS object and properly convert all fields to their correct proto types — exactly what is needed after BufferJSON.reviver deserialization.

Fix

Replace .create() with .fromObject() in the three auth-state files. The logic is extracted into a shared deserializeAppStateSyncKey helper in src/utils/proto-helpers.ts to avoid duplication across providers.

- value = proto.Message.AppStateSyncKeyData.create(value);
+ value = deserializeAppStateSyncKey(type, value);

Files changed:

  • src/utils/proto-helpers.ts (new — shared helper)
  • src/utils/use-multi-file-auth-state-prisma.ts
  • src/utils/use-multi-file-auth-state-provider-files.ts
  • src/utils/use-multi-file-auth-state-redis-db.ts

Verified behavior

After this fix:

  • App-state decryption succeeds (no more bad decrypt)
  • Full snapshot sync processes correctly (363 mutations for regular collection)
  • Delta syncs work on subsequent label changes
  • labels.association, chats.update, and other app-state events fire correctly

Tested with Evolution API v2.3.7, Baileys 7.0.0-rc.9, PostgreSQL auth-state provider.

Reopened from #2593 against develop as requested.

Summary by Sourcery

Use a shared helper to correctly deserialize app-state sync keys for all auth-state providers so app-state events can be decrypted and processed.

Bug Fixes:

  • Fix app-state decryption by using AppStateSyncKeyData.fromObject instead of create when loading stored app-state sync keys.

Enhancements:

  • Introduce a shared deserializeAppStateSyncKey utility to centralize app-state key deserialization across file, Redis, and Prisma auth-state providers.

…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 with BufferJSON.reviver.

The fix is extracted into a shared deserializeAppStateSyncKey helper
in proto-helpers.ts to avoid duplication across the three providers.

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.
@sourcery-ai

sourcery-ai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Replaces direct use of proto.Message.AppStateSyncKeyData.create with a shared helper that uses fromObject for app-state-sync-key deserialization across all auth-state providers, ensuring proper type conversion and fixing app-state decryption, while centralizing the logic in a new proto-helpers utility.

File-Level Changes

Change Details Files
Centralize and fix deserialization of app-state-sync-key data for all auth-state providers using a shared helper that calls AppStateSyncKeyData.fromObject instead of .create.
  • Introduce a new deserializeAppStateSyncKey utility that conditionally converts app-state-sync-key values with proto.Message.AppStateSyncKeyData.fromObject, otherwise returning the input unchanged.
  • Wire the helper into the file-based auth-state provider by replacing inline AppStateSyncKeyData.create handling with a call to deserializeAppStateSyncKey for each loaded key.
  • Update the Redis-backed auth-state provider to use deserializeAppStateSyncKey instead of directly calling AppStateSyncKeyData.create when reading app-state-sync-key values.
  • Update the Prisma-backed auth-state provider to use deserializeAppStateSyncKey in place of AppStateSyncKeyData.create, keeping behavior consistent across all providers.
src/utils/proto-helpers.ts
src/utils/use-multi-file-auth-state-provider-files.ts
src/utils/use-multi-file-auth-state-redis-db.ts
src/utils/use-multi-file-auth-state-prisma.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In deserializeAppStateSyncKey, consider tightening the type parameter to a string literal/union (e.g., 'app-state-sync-key' | ...) and value to a more specific type instead of unknown so callers get better type safety and autocompletion.
  • The providers currently pass raw type strings into deserializeAppStateSyncKey; to avoid subtle bugs from typos in these strings, you might centralize the known key types as an enum or constant map and use that across the auth-state implementations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `deserializeAppStateSyncKey`, consider tightening the `type` parameter to a string literal/union (e.g., `'app-state-sync-key' | ...`) and `value` to a more specific type instead of `unknown` so callers get better type safety and autocompletion.
- The providers currently pass raw `type` strings into `deserializeAppStateSyncKey`; to avoid subtle bugs from typos in these strings, you might centralize the known key types as an enum or constant map and use that across the auth-state implementations.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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.

1 participant