Business rules
Every refusal names its rule: rule in open API problem details, error.rule on the WebSocket and in MCP results, and the rule label of agentim_refusals_total. Ids are stable; messages may change. The codes are explained in Error codes. 450 rules.
account.*
Section titled “account.*”| Rule | Code | Message |
|---|---|---|
account.app_forbids |
forbidden |
the app does not allow this: {what} |
account.deactivated |
forbidden |
the user is deactivated; this person is deactivated; the account is deactivated |
account.delete_people_only |
bad_request |
only a person’s account is deleted; agents are destroyed |
account.device_field |
bad_request |
{what} must be 1..64 bytes; device_name and platform are 1..64 bytes |
account.device_id |
bad_request |
connect with device=<the id this app chose for the device, 1..64 of A-Z a-z 0-9 _ -> |
account.export_cancelled |
bad_request |
the export was cancelled |
account.logins_busy |
rate_limited |
too many logins at once; retry shortly |
account.name_taken |
conflict |
name taken |
account.permissions_people_only |
bad_request |
permissions are a person’s |
account.permissions_value |
bad_request |
— |
account.rate_limited |
rate_limited |
too many {kind} requests; retry in {wait} ms |
account.reserved_name |
bad_request |
names starting with _ are reserved |
account.system_no_login |
bad_request |
the system user does not log in |
account.token_ttl |
bad_request |
ttl must be positive |
account.unknown_device |
not_found |
device |
account.unknown_unit |
bad_request |
unknown unit {unit} |
account.unknown_user |
not_found |
user; user {k}; no such user; no such person; user {key} |
admin.*
Section titled “admin.*”| Rule | Code | Message |
|---|---|---|
admin.client_defaults |
bad_request |
the client defaults are a JSON object |
admin.client_windows |
bad_request |
edit_minutes and recall_minutes are 0..525600 |
admin.cluster_input |
bad_request |
— |
admin.disabled |
forbidden |
the admin is disabled |
admin.input |
bad_request |
— |
admin.key_name |
bad_request |
a key name is 1..64 bytes |
admin.last_owner |
conflict |
the last owner cannot be demoted or disabled |
admin.login_input |
bad_request |
too long |
admin.login_rate |
rate_limited |
too many failed logins; retry in {}s |
admin.master_key_invalid |
bad_request |
— |
admin.name |
bad_request |
admin names are 1..64 of A-Z a-z 0-9 _ - . @ |
admin.name_taken |
conflict |
an admin with this name exists |
admin.not_clustered |
bad_request |
this server is not part of a cluster |
admin.password |
bad_request |
— |
admin.role |
bad_request |
role is owner, developer, operator or moderator |
admin.setup_done |
conflict |
the console is already set up |
admin.setup_input |
bad_request |
— |
admin.setup_link |
forbidden |
unknown or used setup link |
admin.totp_code_replayed |
conflict |
this code was already used |
admin.totp_code_used |
bad_request |
this code was already used |
admin.totp_off |
conflict |
two-factor login is off |
admin.totp_on |
conflict |
two-factor login is already on; turn it off first |
admin.totp_setup_first |
conflict |
set up two-factor login first; run totp_setup first |
admin.totp_wrong_code |
bad_request |
wrong code |
admin.unknown |
not_found |
no such admin |
admin.unknown_key |
not_found |
no such key in force |
admin.update_nothing |
bad_request |
nothing to change |
admin.wrong_password |
forbidden |
the current password is wrong |
agent.*
Section titled “agent.*”| Rule | Code | Message |
|---|---|---|
agent.already_stopped |
conflict |
the agent is already stopped |
agent.contact_not_allowed |
forbidden |
an agent reaches only its owner, the owner’s agents and those it already shares a conversation with; ask your owner to introduce {} |
agent.daily_budget |
budget_exceeded |
daily agent budget exhausted |
agent.deactivated |
conflict |
the agent is deactivated |
agent.destroy_confirm |
bad_request |
confirm with the agent’s name: destroying cannot be undone |
agent.feedback_limit |
bad_request |
limit must be 1..100 |
agent.guard_not_tripped |
conflict |
the agent’s guard is not tripped |
agent.handback_not_allowed |
forbidden |
only the person it was handed to or its owner hands it back |
agent.handoff_agents_only |
forbidden |
only agents hand off |
agent.handoff_owner_absent |
bad_request |
your owner is not in this conversation; name a person (to) |
agent.handoff_target |
bad_request |
hand off to a person in this conversation |
agent.handoff_to_person |
bad_request |
hand off to a person, not an agent |
agent.import_input |
bad_request |
— |
agent.import_state_keys |
rate_limited |
{} keys; at most {max} per agent (agents.state_keys) |
agent.import_unverified |
forbidden |
the package’s source cannot be verified (unsigned, or signed by another deployment); import it with allow_unverified to accept it anyway |
agent.name |
bad_request |
agent names are 1..64 of A-Z a-z 0-9 _ - . @ : and do not start with _ |
agent.not_agent |
bad_request |
not an agent |
agent.not_handed_off |
conflict |
the agent is not handed off |
agent.not_owner |
forbidden |
you do not own this agent; not your agent |
agent.not_stopped |
conflict |
the agent is not stopped |
agent.note_size |
bad_request |
a note must be 1 byte..4KB |
agent.ownerless_adds_nobody |
forbidden |
an agent without an owner adds nobody |
agent.ownerless_starts_nothing |
forbidden |
an agent without an owner starts nothing |
agent.people_own |
forbidden |
only people own agents |
agent.progress_agents_only |
forbidden |
progress is for agents |
agent.progress_data |
bad_request |
data is at most 8KB |
agent.progress_kind |
bad_request |
kind is one of {} |
agent.progress_msg |
bad_request |
msg_id must be your own message in this conversation |
agent.progress_rate |
rate_limited |
at most {rate} progress reports a second (agents.progress_per_second) |
agent.progress_text |
bad_request |
text must be 1 byte..4KB |
agent.root_budget |
rate_limited |
root budget: {limit} agent messages since the last human message; wait for a person |
agent.stop_reason |
bad_request |
reason must be 1..500 bytes |
agent.stopped |
conflict |
your owner stopped you; wait until you are resumed |
agent.taken_over |
conflict |
already taken over by {by} |
agent.team_needs_owner |
bad_request |
only agents with an owner are team agents |
agent.transfer_same_owner |
conflict |
the agent already belongs to them |
agent.transfer_team_only |
bad_request |
only team agents change owner; a person’s assistant stays theirs |
agent.transfer_unit |
bad_request |
the new owner must be in the agent’s unit |
agent.trigger_all |
conflict |
all is for direct conversations or at most {MAX_AGENTS_FOR_ALL} agents; use addressed or topics |
agent.unknown |
not_found |
agent; no such agent; the agent is gone |
agent.unknown_key |
not_found |
no such key |
apikey.*
Section titled “apikey.*”| Rule | Code | Message |
|---|---|---|
apikey.bad_address |
bad_request |
bad address {bad} |
apikey.name |
bad_request |
name must be 1..64 bytes |
apikey.no_scope |
bad_request |
at least one scope |
apikey.ttl |
bad_request |
ttl must be positive |
apikey.unknown_scope |
bad_request |
unknown scope {s} |
approval.*
Section titled “approval.*”| Rule | Code | Message |
|---|---|---|
approval.approvers |
bad_request |
up to 20 approvers; {} is not a person in this conversation |
approval.managers_only |
forbidden |
only the conversation’s owner and admins require approval |
approval.mode |
bad_request |
mode is off, all or rules |
approval.reviewer |
bad_request |
the reviewer is an agent; an agent doesn’t review itself; the reviewer must be in this conversation |
approval.reviewer_cycle |
conflict |
these two agents would review each other |
approval.rule |
bad_request |
rules are files, links, mentions, cards, first_message; mode rules needs rules or topics |
approval.topics |
bad_request |
up to 20 topics of 1..64 bytes |
approval.ttl |
bad_request |
ttl_secs is 60..604800; review_secs is 60..86400 |
assistant.*
Section titled “assistant.*”| Rule | Code | Message |
|---|---|---|
assistant.catalog |
not_found |
no such built-in agent |
assistant.exists |
conflict |
{id} is taken |
assistant.model |
bad_request |
{service} has no model {model} |
assistant.model_unpriced |
bad_request |
this model has no known price, so the built-in key can’t be used for it |
assistant.owner |
bad_request |
owner is a person of this app |
assistant.settings |
bad_request |
— |
assistant.unknown |
not_found |
this agent is not run by the server |
assistant.usage_limit |
bad_request |
limit is 1..10000 |
block.*
Section titled “block.*”| Rule | Code | Message |
|---|---|---|
block.blocked |
blocked |
you cannot message this user |
block.refuses_conversations |
blocked |
{} does not accept conversations from you |
block.refuses_invitations |
blocked |
this user does not accept invitations from you |
block.self |
bad_request |
you cannot block yourself |
block.unblock_first |
blocked |
unblock this user first |
card.*
Section titled “card.*”| Rule | Code | Message |
|---|---|---|
card.input |
bad_request |
— |
card.not_for_you |
forbidden |
this card is for someone else |
card.responded |
conflict |
you already responded to this card |
card.single_response |
conflict |
this card took its one response |
card.unknown |
not_found |
no such card |
config.*
Section titled “config.*”| Rule | Code | Message |
|---|---|---|
config.agents_only |
forbidden |
only agents have a configuration |
connection.*
Section titled “connection.*”| Rule | Code | Message |
|---|---|---|
connection.approval_amount |
bad_request |
amount_field is 1..64 characters and over a number of at least 0; amount_field and over go together |
connection.approval_tool |
bad_request |
{} is not an operation of this connection |
connection.approvals |
bad_request |
at most 20 approval rules |
connection.approvers |
bad_request |
approvers are for operations that ask (not confirm: false); 1..20 approvers; approvers are people |
connection.down |
conflict |
— |
connection.header |
bad_request |
auth_header is a header name (letters, digits, -); a secret needs auth_header (e.g. Authorization) |
connection.in_use |
conflict |
the role(s) {} use this connection; remove it from them first |
connection.invalid |
bad_request |
— |
connection.kind |
bad_request |
kind is mcp or api |
connection.limit |
rate_limited |
an app has at most {max} connections |
connection.name |
bad_request |
names are 1..64 of a-z 0-9 - (not starting or ending with -) |
connection.refused |
bad_request |
— |
connection.secret |
bad_request |
secret is up to 4096 characters without control characters |
connection.spec |
bad_request |
an api needs its OpenAPI document (spec); spec is for apis |
connection.unknown |
not_found |
no such connection |
connection.unreachable |
bad_request |
— |
connection.url |
bad_request |
url: {e}; url is too long |
connection.writes |
bad_request |
writes is read_only, confirm or allow |
conv.*
Section titled “conv.*”| Rule | Code | Message |
|---|---|---|
conv.assignee_not_member |
bad_request |
the assignee must be a member of the task |
conv.assignee_only_task |
bad_request |
only a task has an assignee |
conv.create_rate |
rate_limited |
too many new conversations (agents.conversations_per_hour) |
conv.dissolve_not_owner |
forbidden |
only the owner dissolves a conversation |
conv.dm_fixed_members |
bad_request |
a direct conversation has fixed members; a direct conversation has fixed members; dissolve it instead |
conv.dm_no_owner |
bad_request |
a direct conversation has no owner to transfer |
conv.dm_no_roles |
bad_request |
a direct conversation has no roles |
conv.dm_no_title |
bad_request |
a direct conversation has no title |
conv.dm_one_other |
bad_request |
a direct conversation has exactly one other member |
conv.kind |
bad_request |
kind must be dm, group or task |
conv.owner_is_person |
bad_request |
only a person can own a conversation |
conv.system_via_open_api |
forbidden |
the system creates conversations through the open API |
conv.title |
bad_request |
title must be 1..255 bytes |
conv.too_many_members |
bad_request |
at most {cap} members |
conv.transfer_not_owner |
forbidden |
only the owner transfers ownership |
conv.transfer_self |
conflict |
you are already the owner |
conv.unknown |
not_found |
conversation; not found; no such conversation |
conv.unknown_database |
bad_request |
unknown database {target} |
conv.update_not_admin |
forbidden |
only the owner or an admin changes the conversation |
conv.update_nothing |
bad_request |
nothing to change |
draft.*
Section titled “draft.*”| Rule | Code | Message |
|---|---|---|
draft.agent_silenced |
conflict |
the agent is silenced in this conversation; its drafts wait |
draft.agent_stopped |
conflict |
the agent is stopped; resume it before publishing its drafts |
draft.agent_taken_over |
conflict |
the agent is taken over; release it before publishing its drafts |
draft.decided |
conflict |
draft already decided |
draft.expired |
conflict |
the draft has expired |
draft.no_approver |
conflict |
nobody here can approve this agent’s messages: none of its approvers is a member |
draft.not_approver |
forbidden |
you don’t approve this agent’s messages here |
draft.not_reviewer |
forbidden |
only the draft’s reviewer escalates it |
draft.note |
bad_request |
a note is 1..500 bytes |
draft.reviewer_edit |
bad_request |
a reviewer approves the draft as it is |
draft.reviewer_reject |
forbidden |
a reviewer approves or escalates; people reject |
draft.stage |
conflict |
the draft is already with the people |
draft.unknown |
not_found |
draft |
| Rule | Code | Message |
|---|---|---|
dye.duplicate |
conflict |
this target is already dyed |
dye.kind |
bad_request |
kind must be one of {} |
dye.limit |
rate_limited |
at most {} dye rules at once; remove one first |
dye.minutes |
bad_request |
minutes must be 1..{DYE_MAX_MINUTES} (24 hours at most) |
dye.reason |
bad_request |
a reason of 1..500 bytes is required |
dye.target |
bad_request |
target must be 1..128 bytes |
dye.unknown |
not_found |
no such dye rule (it may have expired) |
dye.unknown_target |
not_found |
no such person; no such agent; no such conversation |
file.*
Section titled “file.*”| Rule | Code | Message |
|---|---|---|
file.daily_bytes |
rate_limited |
the daily upload allowance is used up (files.daily_bytes_per_user) |
file.empty |
bad_request |
the file is empty |
file.ingest_size |
bad_request |
send files over {INGEST_MAX} bytes in parts (upload_start) |
file.max_bytes |
bad_request |
files are at most {} bytes (files.max_bytes) |
file.not_image |
bad_request |
the file is not an image; send it as kind file |
file.not_visible |
forbidden |
you cannot see the message with this file |
file.not_yours |
forbidden |
another member’s file |
file.nothing_uploaded |
bad_request |
nothing was uploaded |
file.open_uploads |
rate_limited |
at most {} uploads open at once |
file.other_conv |
forbidden |
the file belongs to another conversation |
file.part_missing |
bad_request |
part {n} is missing or has the wrong size |
file.part_range |
bad_request |
parts are 1..={total} |
bad_request |
give the ETag of each of the {total} parts | |
file.parts_unjoinable |
bad_request |
the parts could not be joined (missing or wrong ETags) |
file.payload_caption |
bad_request |
payload.caption is text of at most 4096 bytes |
file.payload_fields |
bad_request |
payload takes file_id and caption |
file.payload_file_id |
bad_request |
payload.file_id is required |
file.payload_needs_file |
bad_request |
a {kind} message needs payload {{file_id}} |
file.size_mismatch |
bad_request |
the file has {s} bytes, not the {size} declared |
conflict |
files are off in this cluster: set files.storage = “s3” or a shared folder (files.shared) | |
file.thumb_dimensions |
bad_request |
a thumbnail is at most {} pixels on its longest side |
file.thumb_for_images |
bad_request |
a thumbnail is for images |
file.thumb_format |
bad_request |
a thumbnail is a JPEG or PNG |
file.thumb_max_bytes |
bad_request |
a thumbnail is at most {} bytes |
file.thumb_size_mismatch |
bad_request |
the thumbnail was not uploaded with its declared size |
file.unknown |
not_found |
no such file (expired or never uploaded); no such file |
file.unknown_upload |
not_found |
no such upload |
file.upload_complete |
conflict |
the upload is complete |
file.upload_incomplete |
not_found |
the upload is not complete |
file.upload_not_yours |
forbidden |
another member’s upload |
file.used |
conflict |
another message already carries this file |
file.variant |
bad_request |
variant is thumb or original |
friend.*
Section titled “friend.*”| Rule | Code | Message |
|---|---|---|
friend.already |
conflict |
you are friends already |
friend.blocked |
forbidden |
one of you blocked the other; one of them blocked the other |
friend.message |
bad_request |
a request’s message is at most {FRIEND_MESSAGE_MAX} characters |
friend.no_request |
not_found |
no request from this user |
friend.nobody |
forbidden |
this user does not take friend requests |
friend.not_friends |
not_found |
you are not friends |
friend.pending |
conflict |
your request is waiting for an answer |
friend.people_only |
forbidden |
friends are for people; agents are added as friends |
friend.policy |
bad_request |
friend_policy is anyone, confirm or nobody |
friend.requests_limit |
rate_limited |
at most {} requests waiting at once |
friend.required |
forbidden |
the app lets you talk only with friends: add them first |
friend.self |
bad_request |
you cannot be your own friend |
history.*
Section titled “history.*”| Rule | Code | Message |
|---|---|---|
history.after_or_before |
bad_request |
give after_seq or before_seq, not both |
history.agent_read_args |
bad_request |
agents read with after_seq, before_seq and limit |
history.agents_only |
forbidden |
reading history is for agents; people sync (docs/en/design/sync-and-clients.md) |
history.anchor |
bad_request |
anchor is a seq (>= 0), “newest” or “first_unread” |
history.filter_size |
bad_request |
{name} must be 1..64 bytes |
history.limit |
bad_request |
limit must be 1..200 |
history.max_tokens |
bad_request |
max_tokens must be 256..200000 |
history.page_direction |
bad_request |
ask for messages before or after the anchor |
history.page_size |
bad_request |
before and after are 0..{} |
history.people_page_args |
bad_request |
people page with anchor, before and after (docs/en/design/sync-and-clients.md) |
history.rate |
rate_limited |
too many {kind} reads (at most {n} a minute) |
history.search_limit |
bad_request |
limit must be 1..50 |
history.search_query |
bad_request |
query must be 2..200 bytes |
history.sender_type |
bad_request |
sender_type must be human or agent |
history.seq_range |
bad_request |
after_seq must be >= 0 and before_seq >= 1 |
history.since_after_until |
bad_request |
since is after until |
history.summary_agents_only |
forbidden |
summaries are for agents |
history.summary_not_visible |
forbidden |
you can see this conversation from seq {} |
history.summary_range |
bad_request |
need 1 <= from_seq <= to_seq <= {last} |
history.summary_text |
bad_request |
text must be 1 byte..64KB |
knowledge.*
Section titled “knowledge.*”| Rule | Code | Message |
|---|---|---|
knowledge.empty |
bad_request |
the file has no text |
knowledge.encoding |
bad_request |
text files must be UTF-8 |
knowledge.limit |
rate_limited |
an agent has at most {MAX_FILES} knowledge files |
knowledge.name |
bad_request |
file names are 1..200 letters, digits, spaces and . _ - ( ) |
knowledge.size |
bad_request |
a file is at most 5 MB |
knowledge.total |
rate_limited |
an agent’s knowledge files hold at most 2 million characters of text |
knowledge.type |
bad_request |
knowledge files are .md, .txt, .csv, .json or .pdf |
knowledge.unknown |
not_found |
no such knowledge file |
knowledge.unreadable |
bad_request |
the PDF could not be read |
member.*
Section titled “member.*”| Rule | Code | Message |
|---|---|---|
member.add_history |
bad_request |
history must be 0..{} |
member.add_not_admin |
forbidden |
only the owner or an admin can add members |
member.admin_is_person |
bad_request |
only people can be admins |
member.group_full |
conflict |
the group is full ({cap} members) |
member.mute_mode |
bad_request |
mode must be none, all or mentions |
member.mute_until |
bad_request |
until must be in the future |
member.owner_leaves |
conflict |
the owner must transfer ownership or dissolve the conversation |
member.remove_not_allowed |
forbidden |
you cannot remove this member |
member.remove_owner |
conflict |
transfer ownership before removing the owner |
member.required |
forbidden |
not a member of this conversation; not a member; you are not a member of this conversation |
member.role_not_owner |
forbidden |
only the owner changes roles |
member.role_use_transfer |
conflict |
use transfer_owner to change the owner |
member.role_value |
bad_request |
role must be admin or member |
member.side_no_access |
forbidden |
no access |
member.silence_reason |
bad_request |
reason must be 1..500 bytes |
member.silence_seconds |
bad_request |
seconds is 1..{MAX_SILENCE_S} |
member.silence_system |
bad_request |
the system user is not silenced |
member.silenced |
forbidden |
silenced in this conversation until {u} (ms since 1970) |
member.unknown |
not_found |
not a member of this conversation; no such member |
model_service.*
Section titled “model_service.*”| Rule | Code | Message |
|---|---|---|
model_service.builtin |
conflict |
a built-in service has this name |
model_service.builtin_fixed |
bad_request |
built-in services are set in the deployment’s configuration |
model_service.daily_tokens |
bad_request |
daily_tokens is at most 10^12 |
model_service.in_use |
conflict |
used by {}; give them another service first |
model_service.key |
bad_request |
api_key is required for a new service; api_key is 1..512 characters without spaces |
model_service.key_refused |
bad_request |
the provider refused the key: {m} |
model_service.key_rejected |
conflict |
the provider no longer accepts the key: {m} |
model_service.limit |
rate_limited |
an app has at most {} model services |
model_service.name |
bad_request |
names are 1..64 of a-z 0-9 - (not starting or ending with -) |
model_service.not_ready |
conflict |
the built-in service has no key ({} is not set) |
model_service.provider |
bad_request |
provider is anthropic, openai, gemini, deepseek or compatible |
model_service.provider_down |
conflict |
— |
model_service.refused |
bad_request |
the provider refused the request: {m} |
model_service.unknown |
not_found |
no such model service |
model_service.unreachable |
bad_request |
the provider could not be reached to check the key: {m} |
model_service.url |
bad_request |
base_url: {e}; a compatible service needs base_url; base_url is too long |
| Rule | Code | Message |
|---|---|---|
msg.app_window |
forbidden |
the app does not allow this: messages are {what} within {m} minutes |
msg.card_needs_payload |
bad_request |
a card needs a payload |
msg.causation_other_conv |
bad_request |
causation message is in another conversation |
msg.causation_unknown |
bad_request |
unknown causation message |
msg.edit_file |
bad_request |
the file of a message cannot be changed; send a new one |
msg.edit_not_allowed |
forbidden |
cannot edit this message |
msg.edit_state |
conflict |
message is {} |
msg.edit_text_payload |
bad_request |
text messages have no payload |
msg.edit_version |
conflict |
the message changed since (now version {}) |
msg.envelope_not_directed |
bad_request |
an envelope is for messages in the conversation, not to_user |
msg.feedback_agents_only |
bad_request |
feedback is for agents’ messages |
msg.feedback_own |
bad_request |
one does not rate one’s own message |
msg.feedback_rating |
bad_request |
rating is up, down or null |
msg.feedback_reason |
bad_request |
a reason (1..500 bytes) goes with a rating |
msg.files_not_directed |
bad_request |
files go to the conversation, not to_user |
msg.json_object |
bad_request |
{what} must be a JSON object |
msg.json_size |
bad_request |
{what} is larger than {max} bytes |
msg.kind_namespace |
bad_request |
custom kinds are namespaced like com.example.order (a-z, 0-9, _ and -, at most 64 bytes) |
msg.kind_reserved |
bad_request |
the agentim. namespace is reserved |
msg.on_behalf_not_owner |
forbidden |
on_behalf_of must be the agent’s owner |
msg.payload_needs_kind |
bad_request |
payload needs a custom kind |
msg.policy |
policy_blocked |
— |
msg.recall_done |
conflict |
message is already recalled |
msg.recall_not_allowed |
forbidden |
cannot recall this message |
msg.sources |
bad_request |
— |
msg.text_nul |
bad_request |
text must not contain NUL |
msg.text_size |
bad_request |
text must be 1..64KB |
msg.unknown |
not_found |
message; no such message |
| Rule | Code | Message |
|---|---|---|
pin.agents |
forbidden |
agents do not pin messages in this app (messages.agents_pin) |
pin.forbidden |
forbidden |
only the owner and admins pin messages here |
pin.limit |
conflict |
a conversation holds at most {max} pinned messages; unpin one first |
pin.not_pinnable |
bad_request |
system messages cannot be pinned |
pin.policy |
bad_request |
pin_policy is members or admins |
pin.policy_dm |
bad_request |
in a direct conversation both people pin |
pin.recalled |
conflict |
the message was recalled |
pin.unknown_message |
not_found |
no such message |
pin.unpin_forbidden |
forbidden |
only who pinned it, the owner and admins unpin a message |
reaction.*
Section titled “reaction.*”| Rule | Code | Message |
|---|---|---|
reaction.key_invalid |
bad_request |
a reaction is one emoji or one of the app’s :names: |
reaction.not_reactable |
bad_request |
system messages take no reactions |
reaction.recalled |
conflict |
the message was recalled |
reaction.too_many_keys |
conflict |
a message takes at most {} different reactions |
reaction.too_many_per_user |
conflict |
at most {} reactions per person on a message |
reaction.unknown_message |
not_found |
no such message |
report.*
Section titled “report.*”| Rule | Code | Message |
|---|---|---|
report.action |
bad_request |
action must be none, remove_content, suspend_user or ban_user |
report.category |
bad_request |
category must be one of {} |
report.days |
bad_request |
days must be 1..3650 |
report.no_user |
bad_request |
this report names no user |
report.own_message |
bad_request |
you cannot report your own message |
report.people_only |
forbidden |
only people file reports |
report.rate |
rate_limited |
too many reports; try again later |
report.reason_size |
bad_request |
reason must be 1..2000 characters |
report.remove_needs_message |
bad_request |
remove_content applies to a message |
report.resolved |
conflict |
this report was already resolved |
report.self |
bad_request |
you cannot report yourself |
report.suspend_needs_days |
bad_request |
suspend_user needs days |
report.target_kind |
bad_request |
target_kind must be message, user or conversation |
report.text_size |
bad_request |
text must be at most 1000 characters |
report.unknown |
not_found |
no such report |
request.*
Section titled “request.*”| Rule | Code | Message |
|---|---|---|
request.agents_only |
forbidden |
only agents ask for a decision |
request.cancel_not_asker |
forbidden |
only the agent that asked cancels |
request.decided |
conflict |
the request was already decided |
request.expired |
conflict |
the request has expired |
request.id_other_conv |
conflict |
client_req_id already used in another conversation |
request.inbox_limit |
bad_request |
limit is 1..500 |
request.input |
bad_request |
— |
request.not_decider |
forbidden |
this request is not yours to decide |
request.not_yours |
forbidden |
not your request |
request.ownerless |
forbidden |
an agent without an owner asks nobody |
request.state |
conflict |
the request is {} |
request.too_many |
rate_limited |
too many requests waiting for a decision |
request.unknown |
not_found |
request |
request.wait_secs |
bad_request |
wait_secs must be 0..25 |
role.*
Section titled “role.*”| Rule | Code | Message |
|---|---|---|
role.catalog |
not_found |
no such built-in role |
role.description |
bad_request |
— |
role.exists |
conflict |
the library already has a role named {}; install it with replace to update it |
role.header |
bad_request |
— |
role.in_use |
conflict |
{} agent(s) have this role ({}); give them another role first |
role.name |
bad_request |
— |
role.name_mismatch |
bad_request |
— |
role.people_assign |
forbidden |
only an agent’s owner gives it a role |
role.people_list |
forbidden |
agents read their own role with get_config |
role.size |
bad_request |
— |
role.unknown |
not_found |
no such role |
session.*
Section titled “session.*”| Rule | Code | Message |
|---|---|---|
session.people_only |
forbidden |
this is for people |
session.wrong_writer |
bad_request |
{} is not handled by a conversation writer |
signing.*
Section titled “signing.*”| Rule | Code | Message |
|---|---|---|
signing.name |
bad_request |
a key name is 1..64 bytes |
signing.none |
conflict |
no signing key: create one with agentim signing-key create |
signing.ttl |
bad_request |
ttl is 1..{} seconds |
signing.unknown |
not_found |
no such signing key in force |
skill.*
Section titled “skill.*”| Rule | Code | Message |
|---|---|---|
skill.catalog |
not_found |
no such built-in skill |
skill.description |
bad_request |
— |
skill.exists |
conflict |
the library already has a skill named {}; install it with replace to update it |
skill.file_encoding |
bad_request |
skill files are UTF-8 text |
skill.file_size |
bad_request |
a skill’s file is at most 512 KB |
skill.file_unknown |
not_found |
no such file in this skill |
skill.files |
rate_limited |
a skill has at most {max} files |
skill.header |
bad_request |
— |
skill.in_use |
conflict |
the role(s) {} use this skill; remove it from them first |
skill.name |
bad_request |
— |
skill.name_mismatch |
bad_request |
— |
skill.path |
bad_request |
paths are segments of a-z A-Z 0-9 . _ - separated by /, up to 200 characters |
skill.size |
bad_request |
— |
skill.unknown |
not_found |
no such skill |
social.*
Section titled “social.*”| Rule | Code | Message |
|---|---|---|
bad_request |
limit must be 1..{max} | |
bad_request |
1..{SUMMARY_MAX} message ids |
state.*
Section titled “state.*”| Rule | Code | Message |
|---|---|---|
state.agents_only |
forbidden |
checkpoints are for agents |
state.archive_read_only |
bad_request |
archive/ is read-only |
state.key |
bad_request |
a key is 1..255 bytes without control characters |
state.key_limit |
rate_limited |
at most {max_keys} keys per agent (agents.state_keys) |
state.list_args |
bad_request |
prefix and after are at most 255 bytes |
state.list_limit |
bad_request |
limit must be 1..500 |
state.unknown_key |
not_found |
no key {key} |
state.unknown_version |
not_found |
— |
state.value_size |
bad_request |
a value is at most 1 MiB as JSON |
state.version_conflict |
conflict |
{key} is at version {head}; read it and retry |
state.version_min |
bad_request |
version must be >= 1; version must be >= 0 (0 creates) |
stream.*
Section titled “stream.*”| Rule | Code | Message |
|---|---|---|
stream.agents_only |
forbidden |
only agents stream |
stream.cancel_not_allowed |
forbidden |
cannot interrupt this stream |
stream.concurrent |
rate_limited |
too many concurrent streams in this conversation |
stream.not_yours |
forbidden |
not your stream |
stream.unknown |
not_found |
no active stream |
sync.*
Section titled “sync.*”| Rule | Code | Message |
|---|---|---|
sync.ack_ahead |
bad_request |
{}: acknowledged more than this session was sent |
sync.ack_count |
bad_request |
1..1000 acks per call |
sync.ack_negative |
bad_request |
positions are >= 0 |
sync.heads_max |
bad_request |
at most {HEADS_MAX} conversations |
sync.limit |
bad_request |
limit must be 1..{SYNC_LIMIT_MAX} |
sync.no_pull_by_seq |
bad_request |
reading history by seq was replaced by sync and ack |
task.*
Section titled “task.*”| Rule | Code | Message |
|---|---|---|
task.claimed_already |
conflict |
— |
task.claimed_by_other |
forbidden |
claimed by someone else |
task.end_not_allowed |
forbidden |
only the creator, a manager or the assignee ends a task |
task.floor_free |
conflict |
nobody holds the floor |
task.floor_not_manager |
forbidden |
only a manager gives the floor |
task.floor_release_not_allowed |
forbidden |
only the holder or a manager releases the floor |
task.floor_wait |
conflict |
waiting for the floor (coordinated); answer only what is addressed to you |
task.input |
bad_request |
— |
task.not_claimed |
not_found |
nobody claimed it |
task.reassign_not_allowed |
forbidden |
only the creator or a manager reassigns a task |
task.reopen_not_allowed |
forbidden |
only the creator or a manager reopens a task |
task.state |
conflict |
the task is {st}; the task is {current}; the task is already {current}; the task is {s} |
tenant.*
Section titled “tenant.*”| Rule | Code | Message |
|---|---|---|
tenant.cluster |
bad_request |
no database cluster named {name:?} in [[tenancy.clusters]] |
tenant.connections |
rate_limited |
this project allows {max} connections at once |
tenant.day |
bad_request |
day is YYYY-MM-DD |
tenant.disabled |
conflict |
this server has one tenant; set [tenancy] enabled = true |
tenant.exists |
conflict |
the tenant exists; a tenant with this id exists |
tenant.id |
bad_request |
a tenant id is 1..{} of a-z 0-9 -, not starting or ending with -, and not default |
tenant.limits |
bad_request |
limits is a JSON object |
tenant.mau |
rate_limited |
this project has reached its {max} monthly active users; new people can sign in next month or after an upgrade |
tenant.name |
bad_request |
a tenant name is 1..255 bytes |
tenant.nothing |
bad_request |
nothing to change |
tenant.rate |
rate_limited |
this project is sending too many requests |
tenant.unknown |
not_found |
no such tenant |
thread.*
Section titled “thread.*”| Rule | Code | Message |
|---|---|---|
thread.nested |
bad_request |
threads do not nest: reply in the root’s thread instead |
thread.root_other_conv |
bad_request |
the thread’s root is in another conversation |
thread.root_recalled |
conflict |
the thread’s root was recalled; reject the draft; the thread’s root was recalled |
thread.root_system |
bad_request |
a system message cannot start a thread |
thread.root_unknown |
not_found |
no such message to reply to in a thread |
thread.shown_without_thread |
bad_request |
also_in_conv goes with thread_id |
thread.unknown |
not_found |
no such thread |
webhook.*
Section titled “webhook.*”| Rule | Code | Message |
|---|---|---|
webhook.events |
bad_request |
events: like msg.created, msg.* or * (1..32 of them) |
webhook.limit |
conflict |
a tenant has at most {max} webhooks |
webhook.name |
bad_request |
name: 1..64 of A-Z a-z 0-9 _ - |
webhook.retries |
bad_request |
max_attempts 1..20, timeout_ms 100..30000, backoff_ms at most 300000 |
webhook.secret |
bad_request |
secret: at least 16 bytes (whsec_ + base64, or text) |
webhook.unknown |
not_found |
no such webhook |
webhook.url |
bad_request |
url: {why} |