Skip to content

Conversation

@NathanFlurry
Copy link
Member

Bug

c.key in actor context returns ['orgId/documentId/page'] instead of ['orgId', 'documentId', 'page']

When creating an actor with:

rivetClient.document.getOrCreate([orgId, documentId, documentType])

Expected c.key: [orgId, documentId, documentType] (array of 3 strings)
Actual c.key: ['orgId/documentId/documentType'] (array with 1 joined string)

Root Cause

The manager router was wrapping serialized key strings in single-element arrays (key: [body.key]) instead of deserializing them back to ActorKey arrays using deserializeActorKey().

Fix

  • Import deserializeActorKey from @/actor/keys
  • GET /actors: deserialize key query param
  • PUT /actors: deserialize body.key for getWithKey and getOrCreateWithKey
  • POST /actors: deserialize body.key, generate UUID only if null/undefined

Files Changed

  • rivetkit-typescript/packages/rivetkit/src/manager/router.ts

Bug: c.key returned ['a/b/c'] instead of ['a', 'b', 'c']

The manager router was wrapping serialized key strings in single-element
arrays instead of deserializing them back to ActorKey arrays.

Changes:
- Import deserializeActorKey
- GET /actors: deserialize key query param
- PUT /actors: deserialize body.key for getWithKey and getOrCreateWithKey
- POST /actors: deserialize body.key, generate UUID only if null/undefined
@claude
Copy link

claude bot commented Jan 29, 2026

PR Review: Fix Actor Key Deserialization

Summary

This PR fixes a critical bug where actor keys were being incorrectly wrapped in single-element arrays instead of being properly deserialized. The fix correctly uses deserializeActorKey() to convert serialized key strings back into ActorKey arrays.

Code Quality ✅

Strengths:

  • Root cause correctly identified: The fix properly addresses the serialization/deserialization mismatch
  • Consistent application: All three affected endpoints (GET /actors, PUT /actors, POST /actors) are fixed
  • Proper import: Added deserializeActorKey import at the top of the file (line 9)
  • Clean implementation: The fix is minimal and focused on the specific issue

Observations:

  1. Line 245: Good practice extracting actorKey to a variable to avoid calling deserializeActorKey() multiple times
  2. Lines 292-295: The null/undefined check for key generation is now more explicit and correct

Potential Issues ⚠️

1. Inconsistent handling of body.key type (Minor)

The API schema defines key as:

  • ActorsGetOrCreateRequestSchema: key: z.string() (required)
  • ActorsCreateRequestSchema: key: z.string().nullable().optional()

However, the POST /actors handler checks for both undefined and null:

body.key === undefined || body.key === null

While this is defensive and safe, consider whether both checks are necessary given the schema validation. The .nullable().optional() schema means the value could be:

  • Missing (undefined)
  • Explicitly null
  • A string

So the current implementation is correct and handles all cases properly.

2. deserializeActorKey() handles undefined gracefully (Good)

Looking at keys.ts:31-38, the function already handles undefined and null by returning an empty array []. This means:

  • GET /actors with ?key= would result in [] instead of an error
  • This is likely the desired behavior for optional keys

Security Considerations ✅

No security concerns identified. The deserializeActorKey() function properly escapes special characters and handles edge cases as demonstrated by the comprehensive test suite.

Performance Considerations ✅

  • Minimal performance impact
  • deserializeActorKey() is efficient with a single linear pass through the string (rivetkit-typescript/packages/rivetkit/src/actor/keys.ts:47-88)
  • Extracting to a variable (line 245) avoids redundant deserialization

Test Coverage ⚠️

Missing Tests:
While keys.test.ts has excellent coverage of serialization/deserialization logic, there appear to be no integration tests for the manager router endpoints that would have caught this bug.

Recommendation:
Consider adding integration tests for the manager router that verify:

  1. GET /actors with ?key=orgId/docId/type returns actor with key: ['orgId', 'docId', 'type']
  2. PUT /actors with key: "a/b/c" creates actor with correct array key
  3. POST /actors with key: "x/y/z" creates actor with correct array key
  4. Edge cases like escaped slashes (e.g., "a\\/b/c"['a/b', 'c'])

Best Practices ✅

The code follows the repository's conventions:

  • Uses existing utility functions instead of reimplementing logic
  • Maintains consistent error handling patterns
  • Follows TypeScript typing conventions

Verdict: Approve with Suggestions

Required Changes: None - the fix is correct and addresses the bug

Suggested Improvements:

  1. Add integration tests for manager router endpoints to prevent regression
  2. Consider documenting the key serialization format in a comment near the endpoints for future maintainers

Testing Checklist

Before merging, verify:

  • pnpm test passes for rivetkit package
  • Manual test: Create actor with multi-part key and verify c.key in actor context returns array of strings
  • Manual test: GET /actors with serialized key returns correct actor

Review generated by Claude Code

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 29, 2026

More templates

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@4063

@rivetkit/db

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/db@4063

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@4063

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@4063

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@4063

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@4063

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@4063

@rivetkit/virtual-websocket

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/virtual-websocket@4063

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@4063

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@4063

commit: cf3f362

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.

2 participants