-
Notifications
You must be signed in to change notification settings - Fork 51
Description
PATCH Operations Don't Work in the Azure Cosmos DB Linux Emulator
Description
I ran into an issue where PATCH operations aren't supported in the Azure Cosmos DB Linux emulator. This is blocking my local tests, since PATCH works fine in the production Azure Cosmos DB environment but fails when running against the Linux emulator.
When I try to execute a partial document update using the PATCH API, the emulator returns an error saying "Invalid request format: missing patch operations array."
This forces me to implement workarounds that differ from production code, which defeats the purpose of having a local emulator.
My Setup
- Emulator image:
mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
- Azure Cosmos SDK:
@azure/cosmos
version4.5.1
- Testcontainers:
@testcontainers/azure-cosmosdb-emulator
version11.7.1
- Platform: macOS/Linux (Docker-based development)
- Connection mode: HTTP (Gateway mode)
- Docker version: 28.3.2
What I Expected
PATCH should behave the same way it does in production, following the Azure Cosmos DB Partial Document Update docs. I should be able to use operations like:
set
- update or add a fieldadd
- add to an array or create a fieldincr
- increment a numeric fieldremove
- delete a fieldreplace
- replace a value (strict)move
- move a value from one path to another
This is a GA feature that's been available since 2021, and it works perfectly in production Azure Cosmos DB. The emulator should support it too.
What Actually Happens
The PATCH operation fails immediately with this error:
Error: Invalid request format: missing patch operations array.
at httpRequest (node_modules/@azure/cosmos/src/request/RequestHandler.ts:145:42)
at node_modules/@azure/cosmos/src/retry/retryUtility.ts:124:26
at addDiagnosticChild (node_modules/@azure/cosmos/src/utils/diagnostics.ts:68:22)
at addDiagnosticChild (node_modules/@azure/cosmos/src/utils/diagnostics.ts:68:22)
at ClientContext.patch (node_modules/@azure/cosmos/src/ClientContext.ts:433:24)
at node_modules/@azure/cosmos/src/client/Item/Item.ts:575:20
at withDiagnostics (node_modules/@azure/cosmos/src/utils/diagnostics.ts:140:27)
Full Reproduction Code
Here's a complete test that demonstrates the issue. This test passes against production Azure Cosmos DB but fails against the Linux emulator:
import { CosmosClient, PartitionKeyDefinitionVersion, PartitionKeyKind } from '@azure/cosmos';
describe('Cosmos DB PATCH Operations', () => {
const endpoint = 'https://localhost:8081'; // or your emulator endpoint
const key = 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==';
const dbName = 'test-db';
const containerId = 'test-container';
const client = new CosmosClient({
endpoint,
key,
connectionPolicy: {
connectionMode: 0, // Gateway mode
enableEndpointDiscovery: false,
},
});
beforeAll(async () => {
// Create database
const { database } = await client.databases.createIfNotExists({ id: dbName });
// Create container with partition key
await database.containers.createIfNotExists({
id: containerId,
partitionKey: {
paths: ['/pk'],
kind: PartitionKeyKind.Hash,
version: PartitionKeyDefinitionVersion.V2,
},
});
});
afterAll(async () => {
// Cleanup
try {
await client.database(dbName).container(containerId).delete();
} catch {}
});
it('should support basic PATCH operations', async () => {
const container = client.database(dbName).container(containerId);
// Create initial document
const initialDoc = {
id: 'doc-1',
pk: 'partition1',
value: 1,
status: 'initial',
arr: [1, 2],
nested: {
count: 5,
name: 'test'
},
};
await container.items.create(initialDoc);
// THIS IS WHERE IT FAILS IN THE EMULATOR
// Error: "Invalid request format: missing patch operations array."
await container.item('doc-1', 'partition1').patch([
{ op: 'set', path: '/status', value: 'updated' },
{ op: 'incr', path: '/value', value: 10 },
{ op: 'add', path: '/arr/-', value: 3 },
{ op: 'set', path: '/nested/count', value: 10 },
{ op: 'add', path: '/newField', value: 'new value' },
]);
// Read and verify
const { resource: updated } = await container.item('doc-1', 'partition1').read();
expect(updated.status).toBe('updated');
expect(updated.value).toBe(11); // 1 + 10
expect(updated.arr).toEqual([1, 2, 3]);
expect(updated.nested.count).toBe(10);
expect(updated.newField).toBe('new value');
}, 30000);
it('should support remove operations', async () => {
const container = client.database(dbName).container(containerId);
const doc = {
id: 'doc-2',
pk: 'partition1',
fieldToRemove: 'will be deleted',
fieldToKeep: 'will stay',
};
await container.items.create(doc);
// ALSO FAILS IN EMULATOR
await container.item('doc-2', 'partition1').patch([
{ op: 'remove', path: '/fieldToRemove' },
]);
const { resource: updated } = await container.item('doc-2', 'partition1').read();
expect(updated.fieldToRemove).toBeUndefined();
expect(updated.fieldToKeep).toBe('will stay');
}, 30000);
it('should support complex nested path updates', async () => {
const container = client.database(dbName).container(containerId);
const doc = {
id: 'doc-3',
pk: 'partition1',
metadata: {
tags: ['tag1', 'tag2'],
counts: {
views: 0,
likes: 0
}
}
};
await container.items.create(doc);
// FAILS IN EMULATOR
await container.item('doc-3', 'partition1').patch([
{ op: 'add', path: '/metadata/tags/-', value: 'tag3' },
{ op: 'incr', path: '/metadata/counts/views', value: 1 },
{ op: 'incr', path: '/metadata/counts/likes', value: 5 },
]);
const { resource: updated } = await container.item('doc-3', 'partition1').read();
expect(updated.metadata.tags).toEqual(['tag1', 'tag2', 'tag3']);
expect(updated.metadata.counts.views).toBe(1);
expect(updated.metadata.counts.likes).toBe(5);
}, 30000);
});
How to Run
If you're using Docker:
# Pull and start the emulator
docker run -d -p 8081:8081 \
--name cosmosdb-emulator \
mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
# Run the test
npm test
If you're using Testcontainers (like I am):
import { AzureCosmosDbEmulatorContainer } from '@testcontainers/azure-cosmosdb-emulator';
const cosmosContainer = await new AzureCosmosDbEmulatorContainer(
'mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview'
)
.withProtocol('http')
.withTelemetryEnabled(false)
.start();
const endpoint = cosmosContainer.getEndpoint();
const key = cosmosContainer.getKey();
Impact
This is causing several problems:
- Tests fail locally - All my E2E tests that use PATCH operations fail when running against the emulator
- Can't test PATCH logic - I can't verify that my partial update logic works correctly before deploying to production
- Need workarounds - I have to write fallback code that does read-modify-replace, which is different from production:
// I'm forced to do this in my code:
try {
await container.item(id, pk).patch(operations);
} catch (error) {
// Fallback for emulator - read entire document
const { resource } = await container.item(id, pk).read();
delete resource._rid;
delete resource._self;
delete resource._etag;
delete resource._attachments;
delete resource._ts;
// Apply changes manually
const updated = { ...resource, ...myChanges };
await container.item(id, pk).replace(updated);
}
- Performance mismatch - The workaround uses 3 operations (read + delete metadata + replace) instead of 1 PATCH, so I can't accurately test performance
- Can't test concurrency - PATCH has path-level conflict resolution in multi-region writes, but my workaround doesn't, so I can't test that behavior
- CI/CD blocked - Our GitHub Actions workflows use the emulator, so we can't run integration tests there either
Related Issues
I found this similar issue for the .NET SDK: azure-cosmos-dotnet-v3#2976
It seems like PATCH support has been missing from the Linux emulator for a while now, even though it's been GA in production since 2021.
What I'm Asking For
Please add PATCH API support to the Linux emulator image. It would be great if mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
(or a newer version) could support the same PATCH operations that work in production Azure Cosmos DB.
This would allow developers using macOS, Linux, or Docker-based workflows to properly test their PATCH logic locally before deploying.
Additional Details
- The Windows emulator might already support this (I haven't tested it), but many teams use Docker/Linux for development
- PATCH has been GA since 2021 according to this blog post
- The feature is documented at https://learn.microsoft.com/en-us/azure/cosmos-db/partial-document-update
- It works perfectly in production across all SDKs (.NET, Java, Node.js)
Thanks for looking into this!
This version is more conversational, includes complete reproduction code, and explains the real-world impact from a developer's perspective. Ready to post to GitHub!