calcom/cal.diy · error · BadRequestException

User with id=${newUserId} is not a valid Round Robin host -

Error message

User with id=${newUserId} is not a valid Round Robin host - the user to which you reassign this booking must be one of the booking hosts. Fetch the booking using the GET /v2/bookings/{uid} endpoint and select id of one of the hosts.

What it means

Thrown in reassignBookingToUser's catch block when roundRobinManualReassignment rejects with the literal message "invalid_round_robin_host". NestJS BadRequestException (HTTP 400). It means the newUserId is not one of the designated round-robin hosts for the booking - manual reassignment may only target an existing host. The message itself tells the caller to GET the booking and pick a host id.

Source

Thrown at apps/api/v2/src/platform/bookings/2024-08-13/services/bookings.service.ts:1113

        reassignedById: reassignedByUser.id,
        emailsEnabled,
        platformClientParams,
        actionSource: "API_V2",
        reassignedByUuid: reassignedByUser.uuid,
      });

      const reassigned = await this.bookingsRepository.getByUidWithUser(bookingUid);
      if (!reassigned) {
        throw new NotFoundException(
          `Reassigned booking with uid=${bookingUid} was not found in the database`
        );
      }

      return this.outputService.getOutputReassignedBooking(reassigned);
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === "invalid_round_robin_host") {
          throw new BadRequestException(
            `User with id=${newUserId} is not a valid Round Robin host - the user to which you reassign this booking must be one of the booking hosts. Fetch the booking using the GET /v2/bookings/{uid} endpoint and select id of one of the hosts.`
          );
        }
      }
      throw error;
    }
  }

  async confirmBooking(bookingUid: string, requestUser: ApiAuthGuardUser) {
    const booking = await this.bookingsRepository.getByUid(bookingUid);
    if (!booking) {
      throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`);
    }

    const platformClientParams = booking.eventTypeId
      ? await this.platformBookingsService.getOAuthClientParams(booking.eventTypeId)
      : undefined;

View on GitHub (pinned to 176037d0af)

Solutions

  1. GET /v2/bookings/{uid} and choose newUserId strictly from the returned hosts array.
  2. If you need a non-host user, first add them as a host on the event type, then reassign.
  3. Re-fetch hosts immediately before the call to avoid stale host lists.
  4. Validate the chosen id against the live host set client-side.

Example fix

// before
await apiClient.post(`/v2/bookings/${uid}/reassign-to-user`, { userId: someUserId });

// after
const booking = await apiClient.get(`/v2/bookings/${uid}`).then(r => r.data);
const validHost = (booking.hosts ?? []).find(h => h.id === someUserId);
if (!validHost) throw new Error(`${someUserId} is not a round-robin host of booking ${uid}`);
await apiClient.post(`/v2/bookings/${uid}/reassign-to-user`, { userId: validHost.id });
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the target user is one of the booking's round-robin hosts
const booking = await apiClient.get(`/v2/bookings/${uid}`).then(r => r.data);
const validHost = (booking.hosts ?? []).find(h => h.id === newUserId);
if (!validHost) {
  throw new Error(`User ${newUserId} is not a round-robin host of booking ${uid}; pick from [${(booking.hosts ?? []).map(h => h.id).join(', ')}]`);
}

Type guard

function isRoundRobinHost(userId: number, booking: { hosts?: Array<{ id: number }> }): boolean {
  return (booking.hosts ?? []).some(h => h.id === userId);
}

Try / catch

try {
  await apiClient.post(`/v2/bookings/${uid}/reassign-to-user`, { userId: newUserId });
} catch (err) {
  if (err.response?.status === 400 && /not a valid Round Robin host/i.test(err.response?.data?.message ?? '')) {
    const hosts = await apiClient.get(`/v2/bookings/${uid}`).then(r => (r.data.hosts ?? []).map(h => h.id));
    throw new InvalidHostError(`Pick one of: ${hosts.join(', ')}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: POST reassign-to-user with a newUserId who is a valid user but is NOT in the booking's host set (e.g., a teammate who was never added to the event type, or a user removed from the round-robin pool).

Common situations: Assuming any org member can receive the booking; using the booking owner id when they are no longer an active host; host membership changed between fetching the user and the call.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/741fd4a00de08749. Report an issue: GitHub.