Skip to content

CloudClient._path() TOCTOU race: concurrent callers each call getUserIdentity() before _tenant/_database are set #7494

Description

@tsushanth

Bug

In clients/new-js/packages/chromadb/src/chroma-client.ts, the CloudClient._path() method has the same TOCTOU race as the old client's init() (fixed in #7487).

Affected code (~line 191)

public async _path(): Promise<{ tenant: string; database: string }> {
  if (!this._tenant || !this._database) {      // ← checked before await
    const { tenant, databases } = await this.getUserIdentity();  // ← suspends here
    this._tenant = tenant;                      // ← set after resume
    ...
  }
  return { tenant: this._tenant, database: this._database };
}

When two operations (e.g. two simultaneous collection queries) call _path() before _tenant/_database are populated:

  1. Both callers see !this._tenant → both enter the block
  2. Both await getUserIdentity() → two concurrent auth calls
  3. Both write this._tenant = tenant → last writer wins (no conflict here, but unnecessary double auth round-trip)
  4. If getUserIdentity() returns different values across calls (e.g. due to token rotation), the final _tenant is non-deterministic

Suggested fix

Same lazy-promise pattern used to fix init():

private _pathPromise: Promise<{ tenant: string; database: string }> | undefined;

public async _path(): Promise<{ tenant: string; database: string }> {
  if (!this._pathPromise) {
    this._pathPromise = (async () => {
      const { tenant, databases } = await this.getUserIdentity();
      this._tenant = tenant;
      // ... rest of setup
      return { tenant: this._tenant, database: this._database };
    })();
  }
  return this._pathPromise;
}

This ensures all concurrent callers await the same in-flight resolution.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions