A limit is a value Chickpea refuses to exceed, checked in code at the moment of the request. Where a setting is chosen in Admin and a quota is billed by Cloudflare or Slack, a limit is compiled into the deployed version and is identical on every deployment of it. This page lists the limits an admin or an operator meets, each with its scope and what happens when a request crosses it.
The source of truth is the Chickpea repository. Every value below was read from the file named at the head of its section. One value is operator-configurable and marked as such: the coding sandbox monthly session cap.
Quick reference
| Limit | Value | Scope |
|---|---|---|
| Files read from one message | 4 | per Slack message |
| Bytes across one message’s files | 12 MiB | per Slack message |
| Rows in a native Slack table | 200 | per table |
| Stored Agent memory | 64 KiB | per Agent |
| Agent handle | 80 characters | per Agent |
| Active schedules | 100 | per deployment |
| Active schedules | 20 | per channel |
| Minimum interval between runs | 5 minutes | per schedule |
| Run-now starts in a rolling 24 hours | 10 | per deployment |
| Operations in one management request | 25 | per request |
| Skill directories scanned on import | 40 | per import |
| Coding sandbox sessions | operator setting, uncapped when unset | per calendar month |
Attachments
Enforced in src/slack/attachment-normalization.ts. Attachments are read for the turn and never stored.
| Limit | Value | At the limit |
|---|---|---|
| Files per message | 4 | No file is read. Every file in the message returns attachment_count_limit_exceeded. |
| Bytes per file | 8 MiB (8,388,608 bytes) | The retrieval is bounded to that size, so a larger file fails with a next action of reduce_file_size instead of arriving truncated. |
| Bytes per message | 12 MiB (12,582,912 bytes) | Once the running total is spent, each remaining file returns attachment_aggregate_byte_limit_exceeded. |
| Characters per file after normalization | 32,000 | The file returns attachment_character_limit_exceeded. |
| Characters per message after normalization | 48,000 | The file that crosses the total returns attachment_aggregate_character_limit_exceeded. |
| Pages in a PDF | 100 | The file returns pdf_page_limit_exceeded. |
| Normalization time | 13,000 ms per file in the message | The file returns attachment_deadline_exceeded. |
Files are read in the order they appear on the message, so a small file behind an oversized one can still succeed. Each failure carries a next action: retry, reupload_file, reconnect_slack, convert_file, reduce_file_size, split_file, use_text_pdf, or remove_unsupported_file.
Native Slack tables
Enforced in src/slack/table-presentation.ts, which validates the present_table tool call before Slack sees it.
| Limit | Value | At the limit |
|---|---|---|
| Data rows | 7 to 200 | Six or fewer rows are refused: “Six or fewer data rows should use a compact Markdown table, not a native Slack table.” More than 200 fails schema validation. |
| Columns | 20 | Schema validation fails, for the column list and for any row. |
| Characters across all cells and headers | 20,000 | “Slack table cells exceed the 20,000-character data-table limit.” |
| Characters in one text cell | 1,000 | Schema validation fails. |
| Characters in a column header | 80 | Schema validation fails. |
| Characters in the caption | 200 | Schema validation fails. |
| Page size on an explorable table | 1 to 100 | Schema validation fails. Unset, the page size is the row count or 10, whichever is smaller. |
| Row header column index | 0 to 19 | Schema validation fails. |
| Native tables in one answer | 1 | “A Slack answer may contain at most one native table presentation.” |
| Characters in the Markdown fallback | 12,000 | Rows stop and the fallback ends with _Showing N of M rows._ |
The model is instructed to summarize instead of calling present_table when a result would exceed 200 rows, 20 columns, or 20,000 cell characters.
Agent memory
Enforced in src/memory/validation.ts and src/memory/store.ts. An Agent has one memory, and a remembered entry is appended to it.
| Limit | Value | At the limit |
|---|---|---|
| Description on one write | 512 bytes | memory_content_too_large, “Description must be at most 512 bytes.” |
| Body on one write | 8 KiB (8,192 bytes) | memory_content_too_large, “Body must be at most 8192 bytes.” |
| Stored memory document | 64 KiB (65,536 bytes) | memory_entry_too_large, “Agent memory is too large.” |
Memory content is also rejected for control characters (memory_invalid_control_character) and for credential-like content (memory_credential_rejected), at any size.
Agent handles
Enforced in src/slack/agent-presence/handles.ts. A handle is a Slack user group with no members.
| Limit | Value | At the limit |
|---|---|---|
| Handle length | 80 characters | The normalized handle is truncated to 80 characters, then any trailing - is removed. |
| Characters allowed | a-z, 0-9, _, - |
Every other run of characters becomes a single -. Leading and trailing - are stripped. |
| Empty result | none | A handle that normalizes to nothing becomes agent. |
| Collision suggestions | 3 by default, suffixes -2 through -9999 |
The base is shortened so the suffixed handle still fits in 80 characters. |
Schedules
Enforced in src/routines/limits.ts, checked in src/routines/store.ts, src/routines/schedule.ts, and src/routines/validation.ts. Schedules run on Cloudflare only. Their error codes carry the word routine, which is what a schedule is called in the source.
Capacity
| Limit | Value | At the limit |
|---|---|---|
| Active schedules per deployment | 100 | routine_active_limit, “This deployment has reached its active routine limit.” |
| Active schedules per channel | 20 | routine_channel_limit, “This channel has reached its active routine limit.” |
| Projected starts per schedule per day | 300 | routine_scheduled_capacity, “This schedule exceeds deployment capacity.” |
| Projected starts per deployment per day | 600 | routine_scheduled_capacity, raised when the new schedule’s projection pushes the deployment total past it. |
| Starts in any rolling 15 minutes | 8 | routine_cluster_capacity, at creation (“Too many routines run near that time.”) and at start time (“This deployment has reached its rolling routine start-rate limit.”). |
| Starts in a rolling 24 hours | 610 | routine_total_start_limit, “This deployment has reached its rolling routine start limit.” |
| Run-now starts in a rolling 24 hours | 10 | routine_run_now_limit, “This deployment has reached its rolling run-now limit.” |
| Concurrent runs per deployment | 4 | routine_concurrent_capacity, “This deployment has reached its active routine run limit.” |
| Concurrent runs per schedule | 1 | A second occurrence is not admitted while the first is running. |
Cadence
| Limit | Value | Note |
|---|---|---|
| Minimum interval between occurrences | 5 minutes | A tighter cadence is refused before the schedule is stored. |
| Occurrence projection horizon | 370 days | A schedule with no occurrence in that window is refused as routine_schedule_out_of_range. |
| Reservation lookahead | 48 hours | How far ahead occurrences are reserved against the rolling 15 minute ceiling. |
| Admission grace | 15 minutes | An occurrence older than the grace period is recorded as skipped, with reason admission_grace_expired or, for one-time work, missed_one_time. |
| Occurrence deadline | 15 minutes | The window in which a result may be delivered. |
| Due claims per heartbeat | 25 | The most occurrences one scheduler wake claims. |
| Admission lease, delivery lease | 2 minutes each | How long a claim is held before another attempt may take it. |
| Delivery retries on a Slack rate limit | 1, and only when the retry-after is 30 seconds or less | Nothing else is retried, because a repeat could post a private result twice. |
| Pending recovery delivery held open | 24 hours | A claimed recovery notice that never settles is recorded as unknown. |
Fields
| Field | Limit |
|---|---|
| Name | 80 code points and 320 bytes |
| Description | 280 code points and 1,120 bytes |
| Task text | 8,192 bytes |
| Schedule input as written | 1,024 bytes |
| Normalized schedule JSON | 4,096 bytes |
| Time zone | 128 bytes, and a valid IANA name |
| Originating request text kept as provenance | 8,192 bytes |
| Change key | 1,024 bytes |
| Public error text | 512 bytes |
Retention
| Record | Kept for |
|---|---|
| Schedule metadata | 365 days |
| Schedule actions | 30 days |
| Pending confirmations | 15 minutes to expiry, purged 24 hours later |
Skill import
Enforced in src/config/skill-import.ts, which reads SKILL.md files from a GitHub repository.
| Limit | Value | At the limit |
|---|---|---|
| Skill directories scanned | 40 | The extra directories are not read and the result reports capped: true, so the source can be narrowed with an @skill filter. |
| Characters kept from a description | 1,024 | Truncated, not rejected. |
| Characters kept from instructions | 100,000 | Truncated, not rejected. |
Bytes read from one SKILL.md |
512 KiB | source_too_large, “GitHub skill document is too large to import safely.” |
| Bytes read from repository metadata | 64 KiB | source_too_large, “GitHub repository metadata is too large to import safely.” |
| Bytes read from the repository tree | 16 MiB | source_too_large, “GitHub repository tree is too large to import safely. Use a direct skill-directory URL.” |
| Bytes read from one directory listing page | 4 MiB | source_too_large, “GitHub skill directory is too large to import safely.” |
| Time for one GitHub request | 10,000 ms | The request is aborted. |
The scan cap keeps one import under the Workers subrequest ceiling: resolution costs two subrequests plus one for every skill scanned.
Management requests
Enforced in src/management/contracts.ts and src/management/schemas.ts. One request is one atomic set of operations, whether it arrives from Admin, from Slack, or from the management MCP server.
| Limit | Value | At the limit |
|---|---|---|
| Operations per request | 1 to 25 | “A management request must contain between 1 and 25 operations.” |
| Serialized request | 128 KiB | “The management request is too large.” |
dependsOn entries per operation |
25 | Schema validation fails. |
| Operations a recipe may compile to | 25 | “The recipe compiles to more than 25 operations. Split it into smaller recipes.” |
A request is also refused when its serialized form carries a control character or credential-like content, at any size: “Management operations cannot contain credentials or control characters.”
MCP client registration
Enforced in src/auth/mcp-oauth.ts, for dynamic client registration against the management MCP.
| Limit | Value | At the limit |
|---|---|---|
| Client metadata document | 16 KiB | client_metadata_too_large, HTTP 413. |
| Registration request body | 32 KiB | request_too_large, HTTP 413. |
client_name |
120 characters, non-empty | invalid_client_name. |
redirect_uris |
1 to 10 | invalid_redirect_uri. A URI with a wildcard, credentials, or a fragment is refused, and plain HTTP is refused except on a loopback host. |
response_types |
exactly ["code"] |
unsupported_response_type. |
grant_types |
authorization_code, optionally with refresh_token |
unsupported_grant_type. |
scope |
only chickpea:workspace |
invalid_scope. |
Only public clients register: token_endpoint_auth_method must be none, and a request carrying jwks, jwks_uri, or token_endpoint_auth_signing_alg is refused as public_clients_only.
The coding sandbox
Enforced in src/config/sandbox-settings.ts, src/sandbox/session-cap.ts, and src/sandbox/egress-handler.ts. The coding sandbox is a Cloudflare container and is optional.
| Limit | Value | At the limit |
|---|---|---|
| Container instance type | standard-1 |
Fixed. It is not an operator choice. |
| Sessions per UTC calendar month | operator setting; unset means uncapped | The turn fails with sandbox_session_cap_reached, “The coding workspace monthly session limit has been reached.” |
| Session cap accepted in Admin | 0 to 100,000, where 0 means no cap | Values outside the range are refused. Settings → Coding sandbox → Advanced pre-fills 200 until the cap is saved. |
| Package registry hosts | registry.npmjs.org, pypi.org, files.pythonhosted.org |
Any other host is denied. An unset allowlist permits all three; an explicit empty list blocks all three. |
| Methods allowed to those hosts | GET, HEAD, POST |
Denied as method-denied. |
A retry of the same durable turn reuses its reservation, so one turn cannot spend the monthly cap twice.
Telemetry delivery
Enforced in src/telemetry/client.ts. Product telemetry is advisory and never changes a customer-visible operation.
| Limit | Value | At the limit |
|---|---|---|
| Serialized batch | 32 KiB | The batch is dropped without a request and without a retry. |
| Delivery time | 5,000 ms | The request is aborted. |
Request bodies
Enforced by the bounded reader in src/security/request-body-limit.ts, which rejects a body whose declared Content-Length is malformed and stops reading once the actual bytes cross the limit. A body that is too large returns HTTP 413; a malformed Content-Length returns HTTP 400 on the Slack and MCP paths.
| Route | Limit | Error |
|---|---|---|
/channels/slack/events, /channels/slack/interactions |
1 MiB | 413 |
/mcp |
1 MiB | request_too_large |
| Admin mutation routes | 1 MiB | request_too_large |
/webhooks/composio |
256 KiB | payload_too_large |
| Slack installation routes in Admin | 64 KiB | payload_too_large |
/api/auth/* |
32 KiB | request_too_large |
/auth/mcp/consent |
16 KiB | request_too_large |
/setup/* |
16 KiB | request_too_large |
/admin/setup, /admin/setup/*, /admin/recovery, /auth/slack/install/*, /auth/slack/oidc/*, /auth/slack/invite, /auth/slack/invite/* |
8,192 bytes | invalid_request |
| Team membership updates | 2,048 bytes | invalid_request, HTTP 400 |
On Node
Two groups above do not apply on Node. Schedules are Cloudflare only, and the conversational tool fails closed with routines_unavailable_on_target. The coding sandbox is a Cloudflare container, so its session cap and package registry allowlist have no effect; Node uses the standard sandbox. Every other limit on this page is enforced the same way on both targets.
What is not covered
Model context windows, provider rate limits, and provider token ceilings belong to the model provider, not to Chickpea. Slack’s own limits on message length, user groups, and API rate apply on top of these and are documented by Slack. Cloudflare’s platform limits on Workers, Durable Objects, and D1 apply on top of these and are documented by Cloudflare.
