dexie / dexie/dexie-encrypted

Composite/compound primary keys break encryption: "key path did not yield a value" DataError

Open Beginner friendly
#6 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
29
Forks
23
PR merge metrics
No merged PRs in 30d

Description

## Summary

Tables that use a Dexie composite (compound) primary key like `[id+lang]` fail to write when wrapped with `applyEncryptionMiddleware`. Every `put()` / `bulkPut()` throws:

> DataError: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.

Affects v2.0.0 and v4.2.0-beta.2 (and presumably all versions in between).

## Reproduction

```ts
import Dexie from 'dexie'
import { applyEncryptionMiddleware, cryptoOptions } from 'dexie-encrypted'

const db = new Dexie('repro')
db.version(1).stores({
// Composite primary key:
meta: '[id+lang], last_synced_millis',
})

applyEncryptionMiddleware(
db,
new Uint8Array(32), // any 32-byte key
{ meta: cryptoOptions.NON_INDEXED_FIELDS },
async () => {},
)

await db.table('meta').put({ id: 'foo', lang: 'en', last_synced_millis: 0, payload: 'hello' })
// → DataError: Evaluating the object store's key path did not yield a value
```

The same code works fine if the primary key is changed to a simple `'id'`.

## Root Cause

In `dist/installHooks.js`, `encryptEntity()` reads the primary key:

```js
const primaryKey = 'primKey' in table.schema
? table.schema.primKey.keyPath
: table.schema.primaryKey.keyPath;
```

For a composite key, `primaryKey` is an **array** (e.g. `["id", "lang"]`).

But the comparisons in all three branches treat it as a string:

```js
if (key === primaryKey || indices.includes(key)) { ... } // NON_INDEXED_FIELDS
if (key !== primaryKey && rule.fields.includes(key)) { ... } // ENCRYPT_LIST
if (key !== primaryKey && ...) { ... } // UNENCRYPTED_LIST
```

`"id" === ["id","lang"]` is always `false`, so the composite-key fields are moved into the encrypted blob instead of being kept as plaintext top-level properties. IndexedDB then can't extract the key from the stored record and throws.

## Suggested Fix

Add an array-aware helper and use it in all three branches:

```js
const primaryKey = 'primKey' in table.schema
? table.schema.primKey.keyPath
: table.schema.primaryKey.keyPath;
const isPrimaryKey = (key) =>
Array.isArray(primaryKey) ? primaryKey.includes(key) : key === primaryKey;

// NON_INDEXED_FIELDS branch:
if (isPrimaryKey(key) || indices.includes(key)) { ... }

// ENCRYPT_LIST branch:
if (!isPrimaryKey(key) && rule.fields.includes(key)) { ... }

// UNENCRYPTED_LIST branch:
if (!isPrimaryKey(key) && entity.hasOwnProperty(key) && ...) { ... }
```

I've been running this patch in production via `pnpm patch` for a database with two composite-key tables (one with `[id+lang]`, one with `[category+name+lang]`) and 20+ regular tables — all encryption/decryption works correctly, including queries via `.where('[id+lang]').equals([...])`.

## Environment

- `dexie-encrypted`: 4.2.0-beta.2
- `dexie`: 4.4.2
- Browser: Chromium (also reproducible in Firefox)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in dist/installHooks.js at encryptEntity and reproduce the failure with the composite-key schema shown in the issue. Check all three encryption-option branches and verify that composite primary-key fields remain available to IndexedDB, then confirm put() and bulkPut() work for the example and the listed multi-field keys.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
databases, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.