A2A 1.0 · Ed25519 receipts · SOC 2 in progress
Build October 27, 2026Status

Your agent got a 422 booking_too_long: what the calendar bucket limit actually counts

A 422 booking_too_long is not a rate limit and not a retry signal. Here is exactly what the bucket math counts, which requests trip it, and how to reshape a long booking so it commits on the first call.

A 422 booking_too_long error means your request exceeded the maximum allowed booking duration. Do not retry it. When managing agentic calendar booking limits, retrying the exact same payload with exponential backoff fails every time because parameter validation runs before any data is written.

The fix first: split the booking, do not retry it

AgentDraft caps single bookings at max_booking_minutes (480 minutes by default, which equals 8 hours, buffers included) and at 99 five-minute buckets per API request. This limit exists because the underlying storage transaction writes each bucket as an independent record and caps at 100 items per call. When your agent asks for a 500-minute window, or a 450-minute meeting with 30 minutes of setup buffers on both ends, it attempts to write more than 99 bucket items. The API rejects the request immediately with an HTTP 422 status code and the error code booking_too_long.

The arithmetic is straightforward:

  • AgentDraft's conflict engine locks time in five-minute buckets: a 30-minute booking writes 6 bucket rows, and the 480-minute maximum booking spans 96.
  • If you book a 480-minute block and add a 15-minute post-meeting buffer (3 buckets) and a 10-minute pre-meeting buffer (2 buckets), your agent requests 101 five-minute buckets.
  • Because 101 buckets exceed the 99-bucket transactional ceiling, the validation filter rejects the write before execution.

To resolve this error in production, apply one of three remediation steps in your agent's tool execution logic:

  1. Shorten the meeting duration so the total span (including pre- and post-buffers) occupies 99 or fewer five-minute increments.
  2. Reduce or eliminate pre-meeting and post-meeting buffers to fit the booking within the 99-bucket limit.
  3. Split the extended reservation into two or more sequential booking requests and commit them in chronological order.

AgentDraft holds expire after 30 seconds by default. A committed booking can be bumped by a higher-priority agent only within a 30-second window, after which it is frozen. A single booking is capped at 480 minutes (8 hours), buffers included, and at 99 five-minute buckets per request; a longer request is rejected with 422 booking_too_long. The cap is service-wide; no workspace or plan setting raises it. Changing paid tiers will not alter this behavior. The adjustment must occur in your agent's scheduling code.

Why agentic calendar booking limits are counted in buckets, not minutes

Calendar bucket limits operate on five-minute boundaries. Look at how the conflict engine works at the storage layer. AgentDraft coordinates holds and commits through a priority-aware conflict engine so multiple agents can act on the same calendar without double-booking.

The conflict engine is race-free at the storage layer, not in application code. A booking writes one time-bucket row per 5-minute bucket (so a 30-minute booking writes 6 rows) inside a single DynamoDB TransactWriteItems, and each write carries a ConditionExpression encoding the priority rule, so two agents committing the same slot cannot both win. Pushing concurrency evaluation into the database transaction eliminates distributed race conditions across autonomous workers.

According to the official AWS DynamoDB API Reference, a TransactWriteItems call accepts at most 100 action items per atomic request. Because AgentDraft reserves one item slot in the transaction for the primary booking metadata entity, exactly 99 transaction slots remain available for the individual time-bucket records. Storing slots in five-minute rows enables granular conflict resolution without bloating the database. If the engine tracked every minute as an individual item, an ordinary two-hour appointment would require 120 items, breaking the database transaction limit.

Review the AgentDraft specification for details on transaction schemas, and consult the entry on multi-agent calendar collisions for examples of priority-based contention handling.

As defined in the RFC 9110 specification for 422 Unprocessable Content, the server understands the content type and syntax of the request entity but cannot process the contained instructions. An invalid payload structure will not self-heal. Backing off and retrying does not resolve it.

Reading the 422 response: what the body tells you

When an agent submits an oversized booking payload, the API returns a structured JSON payload detailing the rejected request bounds:

{
  "error": {
    "code": "booking_too_long",
    "message": "The requested booking duration exceeds the transactional bucket limit.",
    "details": {
      "requested_minutes": 510,
      "buffer_minutes": 30,
      "total_buckets": 108,
      "max_allowed_buckets": 99,
      "max_booking_minutes": 480
    }
  }
}

This validation check is deterministic and runs pre-write. When this response is returned, the database state remains completely untouched. No rows are inserted, no temporary holds are created, and no audit traces require manual revocation. This differentiates the error from intermediate scheduling failures, such as holds that expire due to a 30-second TTL timeout or bookings that are bumped by an agent with higher priority during the 30-second bump window.

To simplify debugging, log these fields whenever an agent encounters a 422 error:

  • The error code string booking_too_long.
  • The total calculated bucket count (total_buckets).
  • The configured max_booking_minutes value returned in the response details.
  • The caller identity extracted from the agent's authentication headers.

AgentDraft agents authenticate with bearer API keys prefixed avs_live_, stored argon2id-hashed. Each key carries explicit scopes (availability:read, bookings:read, bookings:write, mailbox:read, mailbox:write, rules:read, approvals:request); new keys default to availability:read and bookings:write, and approvals:request must be granted per key. If an agent lacks the bookings:write scope, the call is rejected with a 403 Forbidden status before parameter validation occurs, distinguishing authorization errors from structural payload issues.

Buffers are the usual culprit, not the meeting length

Most booking_too_long errors do not stem from single eight-hour meetings. They typically occur when automated scheduling workflows add padding around standard calendar events.

Buffers consume storage capacity fast. An agent configured to add 15 minutes of buffer time before and after a meeting extends a 60-minute discussion into a 90-minute blocked interval. That stretches the required capacity from 12 buckets to 18. When scheduling extended blocks like full-day workshops, architectural reviews, or executive interviews, buffers frequently trigger transaction limits unexpectedly.

Event DurationPre-BufferPost-BufferTotal DurationBuckets RequiredTransaction Status
30 minutes0 minutes0 minutes30 minutes6Accepted
60 minutes15 minutes15 minutes90 minutes18Accepted
240 minutes30 minutes30 minutes300 minutes60Accepted
450 minutes15 minutes15 minutes480 minutes96Accepted
480 minutes10 minutes10 minutes500 minutes100Rejected (422 booking_too_long)
495 minutes0 minutes0 minutes495 minutes99Accepted (if max_booking_minutes >= 495)

Calculate bucket counts programmatically inside your agent's parameter parser prior to issuing outbound network requests:

def calculate_booking_buckets(duration_min: int, pre_buffer: int = 0, post_buffer: int = 0) -> int:
    total_time = duration_min + pre_buffer + post_buffer
    # Buckets are discrete 5-minute allocations
    return (total_time + 4) // 5

def validate_booking_payload(duration_min: int, pre_buffer: int = 0, post_buffer: int = 0) -> bool:
    buckets = calculate_booking_buckets(duration_min, pre_buffer, post_buffer)
    if buckets > 99 or (duration_min + pre_buffer + post_buffer) > 480:
        return False
    return True

Silently reducing buffer durations can introduce scheduling conflicts with other tasks. If a user expects 30 minutes of travel time before an on-site consultation, stripping the buffer to clear validation violates their schedule. When a booking exceeds 99 buckets, split the request into distinct sessions. Do not silently truncate buffers.

Splitting a long booking without breaking race safety

Because each database transaction caps at 99 buckets, bookings exceeding this length must be split across multiple API calls. Each split segment operates as an independent transaction, which changes how atomicity works across the total booking window.

To prevent split bookings from creating fragmented schedule blocks, commit all segments in strict chronological order. If segment two fails due to a collision, the agent should stop immediately. This leaves a single, contiguous reservation block. Discontinuous holds separated by dead space are avoided.

When an agent commits an early segment, it must account for priority bumping. A committed segment older than 30 seconds freezes and cannot be evicted by a higher-priority agent. If a long reservation split into three segments fails on the final leg, rolling back the initial segments requires explicit cancellations. DynamoDB does not roll back earlier transactions automatically.

Use a two-phase hold-then-commit pattern to preserve race safety across multi-segment schedules:

  1. Issue hold requests sequentially for each segment: Segment A (09:00 to 13:00) and Segment B (13:00 to 17:00). Holds enforce the 30-second TTL default.
  2. If any hold request fails, release the acquired holds immediately and halt the workflow. Do not attempt partial commits.
  3. If all holds succeed within the 30-second window, issue commit requests for the held segments in order.

According to guidance from the AWS Knowledge Center, database TTL deletion mechanisms operate asynchronously, meaning expired rows can persist briefly until internal cleanup sweeps run. AgentDraft enforces TTL boundaries on read as well as on write, ensuring expired holds never block incoming high-priority reservations even if storage-layer sweeps have not fully cleared the tombstoned items. The underlying TTL mechanics can be reviewed in the AWS SDK for Kotlin Documentation.

Where the limit sits relative to holds, TTLs, and priority

Designing workflows around agentic calendar booking limits requires accounting for three core system invariants:

  • The conflict engine operates on five-minute bucket boundaries, so all bookings and buffers must snap to integer multiples of five minutes.
  • Hold reservations expire after 30 seconds under the default TTL unless converted into confirmed bookings.
  • High-priority agents can preempt confirmed reservations made by lower-priority agents only during the initial 30-second bump window, after which the booking locks permanently.

AgentDraft evaluates the 99-bucket limit at the parameter validation stage before attempting any storage operations or locking any slots. Rejected requests do not consume concurrency slots or trigger audit entries for dropped state. However, if an agent splits an oversized reservation into multiple valid calls, each successful segment begins its own 30-second bump window immediately upon creation.

AgentDraft publishes a public conflict-resolution benchmark for its own engine; it does not provide load-testing or throughput stress-testing tools for your architecture. Review the AgentDraft benchmark results to explore transaction performance metrics across high-concurrency environments.

Diagnosing the other 422s before you blame the bucket cap

The 422 Unprocessable Entity status code covers multiple request validation errors. Before assuming an issue stems from bucket counts, verify the error.code string returned in the payload.

{
  "error": {
    "code": "invalid_time_range",
    "message": "The booking end time must occur after the start time."
  }
}

Common validation errors sharing the 422 status include:

  • invalid_time_range: The requested start timestamp is set after the end timestamp, or the format violates the W3C ISO 8601 profile for UTC timestamps.
  • unaligned_bucket_boundary: The requested timestamp does not align cleanly with five-minute intervals (for example, attempting to hold an event at 14:02:00 instead of 14:00:00 or 14:05:00).
  • past_time_unsupported: The start time is in the past.
  • unknown_calendar_id: The targeted calendar identifier does not map to a connected account inside the workspace.

AgentDraft syncs Google Calendar today; Microsoft 365 / Outlook calendar sync is planned, not yet shipped. Upstream Google Calendar synchronization issues surface as provider failures. They do not emit standard 422 parameter validation codes. Assert on explicit error.code strings in test suites. Checking the HTTP 422 status code alone is insufficient. The AgentDraft changelog documents all modifications made to validation rules and API error structures.

What to put in your agent's booking code so this never pages you again

Integrate a pre-flight validator directly into your agent's scheduling actions to catch transaction violations before invoking network calls:

from typing import Optional
from dataclasses import dataclass

@dataclass
class ScheduleRequest:
    start_epoch: int
    duration_minutes: int
    pre_buffer_minutes: int = 0
    post_buffer_minutes: int = 0

class AgentBookingTool:
    MAX_BUCKETS = 99
    MAX_MINUTES = 480

    def prepare_booking(self, req: ScheduleRequest) -> dict:
        total_minutes = req.duration_minutes + req.pre_buffer_minutes + req.post_buffer_minutes
        
        # Enforce boundary snapping
        if req.start_epoch % 300 != 0:
            raise ValueError("Start time must align to a 5-minute bucket (300 seconds).")

        # Validate against hard constraints
        buckets = (total_minutes + 4) // 5
        if buckets > self.MAX_BUCKETS or total_minutes > self.MAX_MINUTES:
            return {
                "action": "split_required",
                "total_buckets": buckets,
                "total_minutes": total_minutes,
                "suggested_segments": self._calculate_split(req)
            }
        
        return {
            "action": "proceed",
            "buckets": buckets,
            "payload": {
                "start": req.start_epoch,
                "duration": req.duration_minutes,
                "pre_buffer": req.pre_buffer_minutes,
                "post_buffer": req.post_buffer_minutes
            }
        }

    def _calculate_split(self, req: ScheduleRequest) -> list:
        # Splits long operations across 240-minute blocks
        segments = []
        remaining = req.duration_minutes
        current_start = req.start_epoch
        while remaining > 0:
            segment_duration = min(remaining, 240)
            segments.append({
                "start": current_start,
                "duration": segment_duration
            })
            remaining -= segment_duration
            current_start += (segment_duration * 60)
        return segments

When an agent plans an extended reservation, surface the bucket calculation inside the agent's internal reasoning logs and downstream audit metadata. This ensures human reviewers understand the structural footprint of the booking.

AgentDraft lets an agent pause any consequential action for human sign-off: it opens an approval request carrying a one-line summary and a JSON evidence payload, a person approves or denies it in the dashboard with an optional note, and the agent reads the outcome back. The gated action does not have to be one AgentDraft performs: a deploy, a migration, or a refund is gated the same way. Every transition lands in the append-only audit trail and fires an approval.* webhook.

The requesting agent decides for itself when to open an approval request. AgentDraft does not yet provide a policy engine that auto-requires approval by action class, amount threshold, or role, and there are no escalation chains or multi-approver quorums; a single workspace human resolves each request. Approvals are decided in the AgentDraft dashboard. AgentDraft emails the workspace owner a notification linking to the queue, but the decision itself is made signed in: there are deliberately no approve-from-email links, because an unauthenticated one-click approve is an attack surface. Slack, Discord, Teams, SMS and push delivery are not available today.

When engineering test suites, include assertions verifying that a 481-minute booking triggers booking_too_long, while an isolated 480-minute booking without buffers executes cleanly.

Keep your plan limits in mind when testing: AgentDraft's free Developer tier includes 50 bookings a month. Splitting an extended reservation into three segments consumes three bookings against this quota.

Frequently asked questions

What causes a 422 booking_too_long error?

The error occurs when the requested duration, including pre-meeting and post-meeting buffers, exceeds either 480 minutes or 99 five-minute buckets. The request is rejected during initial validation without modifying calendar state.

Does a rejected booking_too_long request leave a hold or a partial booking behind?

No. The bucket limit check executes prior to generating transaction actions or touching storage. A rejected request leaves behind no database records, no active holds, and no dangling locks.

How many five-minute buckets does a 30-minute booking with buffers use?

It depends on buffer length. The meeting duration takes 6 buckets (30 divided by 5). Adding a 15-minute pre-buffer (3 buckets) and a 15-minute post-buffer (3 buckets) yields 12 buckets in total, which translates to 60 minutes of locked time.

Is booking_too_long a rate limit I should retry with backoff?

No. It is a deterministic payload validation error. The request will fail indefinitely until your code modifies the requested window or splits the booking across multiple transactions.

To inspect your workspace limits or configure integration rules across developer sandboxes, review the calendar API for agents documentation. Test the conflict engine directly with AgentDraft on the AgentDraft Developer tier, which requires no card and includes 1 user, 3 agents, and one mailbox per workspace.