Mã lỗi¶
Mỗi lỗi có một code ổn định. Danh mục dưới đây được lấy nguyên từ
docs/api-errors.md;
backend/tests/test_api_envelope.py đối chiếu file này với
backend/app/core/errors.py và cả hai file ngôn ngữ của UI, nên danh mục luôn
đầy đủ. Thông điệp trong bảng là tiếng Anh; UI hiện bản dịch tiếng Việt theo
code.
Hình dạng response có ở HTTP API.
WebSocket /ws/run error events¶
{"type": "error", "error": {"code": "NODE_FAILED", "message": "...", "details": [...]}, "errors": ["..."], "node": "classify", ...}
errors (a list of strings) is kept for the trace UI and stored history. An auth failure still closes with code 4401. A timeout event carries status: "timeout" and code RUN_TIMEOUT.
Code catalog¶
| Code | HTTP | English message |
|---|---|---|
VALIDATION_ERROR |
422 | Request validation failed |
BAD_REQUEST |
400 | Bad request |
NOT_FOUND |
404 | Resource not found |
METHOD_NOT_ALLOWED |
405 | Method not allowed |
PAYLOAD_TOO_LARGE |
413 | Payload too large |
INTERNAL_ERROR |
500 | Internal server error |
AUTH_REQUIRED |
401 | Authentication required |
AUTH_TOKEN_INVALID |
401 | Invalid token |
PAT_NAME_TAKEN |
409 | You already have an active token named |
PAT_NOT_FOUND |
404 | Token not found |
CLI_DEVICE_INVALID |
400 | This sign-in code is not valid |
CLI_DEVICE_EXPIRED |
400 | This sign-in code has expired |
CLI_DEVICE_DENIED |
400 | This sign-in was denied |
AUTH_TOKEN_EXPIRED |
401 | Session expired, please sign in again |
AUTH_INVALID_CREDENTIALS |
401 | Incorrect email or password |
AUTH_EMAIL_INVALID |
400 | Invalid email address |
AUTH_EMAIL_TAKEN |
409 | Email is already registered |
AUTH_PASSWORD_TOO_SHORT |
400 | Password must be at least {min} characters |
AUTH_PASSWORD_TOO_LONG |
400 | Password must be at most {max} characters |
AUTH_PASSWORD_WEAK |
400 | Password must contain both letters and digits |
AUTH_LOCKED |
429 | Too many failed sign-ins. Try again in {minutes} min |
AUTH_PASSWORD_WRONG |
400 | The current password is incorrect |
AUTH_PASSWORD_SAME |
400 | The new password must be different from the current one |
AUTH_CSRF |
403 | Missing X-Requested-With header |
PASSWORD_RESET_INVALID |
400 | This reset link is invalid or has expired |
WORKFLOW_NOT_FOUND |
404 | Workflow not found |
WORKFLOW_FORBIDDEN |
403 | You do not have access to this workflow |
WORKFLOW_NAME_REQUIRED |
400 | Workflow name is required |
WORKFLOW_NAME_TOO_LONG |
400 | Workflow name must be at most {max} characters |
WORKFLOW_NAME_TAKEN |
409 | A workflow with this name already exists |
AGENT_HANDOFF_TARGET_INVALID |
400 | Agent node {node}: hand-off target {target} must be another Agent node of this workflow, listed once |
AGENT_HANDOFF_LIMIT |
400 | Max hand-offs per run must be 0-{max} |
WORKFLOW_TOO_LARGE |
413 | Workflow is too large (max {max_kb} KB) |
WORKFLOW_INVALID |
400 | Workflow is invalid |
TEMPLATE_NOT_FOUND |
404 | Template not found |
TEMPLATE_READ_ONLY |
403 | This template cannot be deleted |
TEMPLATE_INVALID |
400 | Not a valid template file |
TEMPLATE_NAME_REQUIRED |
400 | Template name is required |
TEMPLATE_SCOPE_FORBIDDEN |
403 | Only administrators can share templates with the whole organisation |
RUN_REQUEST_INVALID |
400 | Run request must be a JSON object |
RUN_INPUT_INVALID |
400 | Run input must be a JSON object |
RUN_INPUT_TOO_LARGE |
413 | Run input is too large (max {max_kb} KB) |
RUN_FAILED |
500 | Run failed |
RUN_TIMEOUT |
408 | Run exceeded the time limit ({seconds}s, STUDIO_RUN_TIMEOUT) |
RUN_NOT_FOUND |
404 | Run not found, already finished or not yours |
THREAD_NOT_FOUND |
404 | Thread not found or not yours |
THREAD_EXISTS |
409 | A thread with this id already exists |
THREAD_BUSY |
409 | The thread already has a run in progress |
THREAD_WORKFLOW_MISMATCH |
409 | The thread belongs to another workflow |
LANGSMITH_NOT_CONFIGURED |
400 | LangSmith is not configured: set LANGSMITH_API_KEY on the server |
LANGSMITH_PUSH_FAILED |
502 | Sending to LangSmith failed ({reason}) |
RUN_NODE_NOT_REACHED |
400 | The run finished without reaching node '{node}' |
NODE_FAILED |
500 | Node failed |
MODEL_POLICY_EXPORT_UNAVAILABLE |
400 | Model fallback policies are not yet supported by code exports; remove the policy to export |
HTTP_TOOL_URL_BLOCKED |
400 | HTTP Tool node {node}: the URL must be a public http(s) address |
TEMPLATE_REF_INVALID |
400 | Template error: |
CREDENTIAL_NOT_FOUND |
404 | Credential not found or not yours |
CREDENTIAL_PROVIDER_UNSUPPORTED |
400 | Unsupported provider: |
CREDENTIAL_KEY_REQUIRED |
400 | API key is required |
CREDENTIAL_BASE_URL_INVALID |
400 | Base URL must start with http:// or https:// |
CREDENTIAL_DECRYPT_FAILED |
500 | Could not decrypt credential (was the secret key changed?) |
PROVIDER_UNREACHABLE |
502 | Could not reach {provider} at {base_url}: is the server running and reachable from the API? |
PROVIDER_AUTH_FAILED |
400 | {provider} rejected the API key: |
PROVIDER_BAD_RESPONSE |
502 | {provider} returned an unexpected response: |
MODEL_NOT_FOUND |
400 | {provider} has no model named |
CREDENTIAL_HEADER_UNSUPPORTED |
400 | {provider} does not accept the header |
PROVIDER_EMBEDDINGS_UNSUPPORTED |
400 | {provider} does not provide embeddings; choose another credential for this knowledge base |
AGENT_TOOLS_UNSUPPORTED |
400 | {provider} cannot call tools; choose a credential with tool calling for this Agent node |
WORKFLOW_TOOL_TOO_DEEP |
400 | Workflow tools nest more than {max} runs deep; this call was not started |
KB_EMBEDDING_DIMENSION_MISMATCH |
409 | The embedding model returns {got}-dimensional vectors but this knowledge base stores {expected}-dimensional ones; re-index the knowledge base |
KB_NOT_FOUND |
404 | Knowledge base not found or not yours |
KB_NAME_REQUIRED |
400 | Knowledge base name is required |
KB_NAME_TOO_LONG |
400 | Knowledge base name must be at most {max} characters |
KB_DOCUMENT_NOT_FOUND |
404 | Document not found |
KB_FILE_REQUIRED |
400 | Choose a file to upload |
KB_FILE_TYPE_UNSUPPORTED |
400 | Unsupported file type (allowed: {allowed}) |
KB_FILE_TOO_LARGE |
413 | File is too large (max {max_mb} MB) |
KB_FILE_UNREADABLE |
400 | Could not read the file: |
KB_FILE_EMPTY |
400 | The file contains no text |
KB_QUERY_REQUIRED |
400 | Enter a search query |
KB_EMBEDDING_FAILED |
502 | Embedding failed: |
KB_EMBEDDER_CHANGED |
409 | The embedding model or key changed since the documents were indexed; re-index the knowledge base |
MCP_SERVER_NOT_FOUND |
404 | MCP server not found |
MCP_NAME_REQUIRED |
400 | MCP server name is required |
MCP_NAME_TOO_LONG |
400 | MCP server name must be at most {max} characters |
MCP_TRANSPORT_UNSUPPORTED |
400 | Unsupported MCP transport: |
MCP_CONFIG_INVALID |
400 | MCP server setting {field} is missing or invalid |
MANAGED_OPERATION_KEY_REQUIRED |
400 | Managed writes require a stable operation ID from a workflow run |
MANAGED_EXPORT_UNAVAILABLE |
400 | Managed connections require Studio operation tracking and cannot yet be exported as standalone code |
MANAGED_OPERATION_CONFLICT |
409 | Operation ID or connection target changed; restore the original configuration to reconcile |
MANAGED_OUTCOME_UNKNOWN |
409 | Operation {operation} has an unknown external outcome; reconcile it before starting another write |
MCP_URL_INVALID |
400 | The MCP server URL must be a public http(s) URL |
MCP_STDIO_DISABLED |
403 | stdio MCP servers are disabled on this server (STUDIO_MCP_STDIO) |
MCP_ADMIN_REQUIRED |
403 | Only an admin can manage stdio or organization MCP servers |
MCP_CONNECT_FAILED |
502 | Could not talk to the MCP server: |
MCP_TOOL_NOT_FOUND |
404 | The MCP server has no tool named |
MCP_TOOL_FAILED |
502 | The MCP tool failed: |
MCP_ARGUMENT_INVALID |
400 | Argument {name} must be of type {type} |
IMPORT_SOURCE_REQUIRED |
400 | A GitHub URL or a zip file is required |
IMPORT_ZIP_TOO_LARGE |
413 | Zip file is too large (max 50 MB) |
IMPORT_INVALID |
400 | Import failed: |
IMPORT_JOB_NOT_FOUND |
404 | Import job not found |
IMPORT_NOT_READY |
400 | Import job is not ready or the graph is invalid |
IMPORT_DISABLED |
404 | Repo import is disabled (set STUDIO_ENABLE_IMPORT=1 to enable this experimental feature) |
AUTH_ACCOUNT_DISABLED |
403 | This account has been deactivated |
PERMISSION_DENIED |
403 | Your role does not allow this ({permission}) |
USER_NOT_FOUND |
404 | User not found |
USER_ROLE_INVALID |
400 | Role must be one of: |
USER_LAST_ADMIN |
409 | The studio needs at least one active admin |
USER_SELF_CHANGE |
409 | You cannot change your own role or deactivate your own account |
SHARE_USER_NOT_FOUND |
404 | No active account with that email |
SHARE_ROLE_INVALID |
400 | Share role must be viewer or editor |
SHARE_OWNER |
400 | The owner already has full access |
SHARE_NOT_FOUND |
404 | This workflow is not shared with that user |
SLACK_OAUTH_NOT_CONFIGURED |
503 | Add to Slack is not configured (SLACK_CLIENT_ID / SLACK_CLIENT_SECRET) |
SLACK_OAUTH_STATE_INVALID |
400 | The Slack sign-in link is invalid or expired, please try again |
SLACK_OAUTH_FAILED |
502 | Slack did not complete the installation: |
TEAM_NOT_FOUND |
404 | Team not found |
TEAM_NAME_REQUIRED |
400 | Team name is required |
TEAM_NAME_TAKEN |
409 | A team with this name already exists |
TEAM_MEMBER_NOT_FOUND |
404 | This user is not a member of the team |
TRANSFER_SAME_OWNER |
400 | This user already owns the workflow |
TRANSFER_TARGET_ROLE |
400 | The new owner must be an editor or an admin |
TRANSFER_REBIND_INVALID |
400 | Node {node}: choose a resource the new owner can use |
USER_HAS_WORKFLOWS |
409 | This user owns {count} workflow(s): choose who takes them over first |
USER_DELETE_MODE_INVALID |
400 | Mode must be anonymize or delete |
WORKFLOW_IN_USE |
409 | {count} workflow(s) use this workflow as a subgraph |
ORG_RESOURCE_NOT_ALLOWED |
403 | Your role may not use organization-wide resources at run time ({kind}) |
CREDENTIAL_SETTINGS_INVALID |
400 | Check the setting |
EMAIL_HOST_BLOCKED |
400 | The SMTP host {host} is a private or local address (allowed only with STUDIO_SMTP_ALLOW_PRIVATE=1) |
EMAIL_AUTH_FAILED |
400 | The SMTP server rejected the sign-in: |
EMAIL_SEND_FAILED |
502 | The e-mail could not be sent: |
EMAIL_CREDENTIAL_INVALID |
400 | Send email node {node}: choose an SMTP credential |
EMAIL_RECIPIENT_INVALID |
400 | Send email node {node}: '{address}' is not an e-mail address |
EMAIL_RECIPIENTS_MISSING |
400 | Send email node {node}: there is no recipient (To, Cc or Bcc) |
EMAIL_TOO_MANY_RECIPIENTS |
400 | Send email node {node}: at most {max} recipients |
EMAIL_RATE_LIMITED |
429 | Send email nodes may reach {limit} recipients per hour; try again in {retry} s |
DEMO_CODE_DISABLED |
403 | Code nodes are turned off in the public demo |
DEMO_NODE_DISABLED |
403 | {node} nodes are turned off in the public demo |
DEMO_READ_ONLY |
403 | The public demo is read-only: you can open, edit and run workflows, but not save changes |
CODE_RUNTIME_OFF |
400 | Code nodes are turned off on this server (STUDIO_ENABLE_CODE_NODES) |
CODE_RUNTIME_UNAVAILABLE |
503 | Code nodes cannot run on this server: |
CODE_INVALID |
400 | Code node {node}: {reason} |
CODE_FAILED |
400 | Code node {node} raised {error} |
CODE_TIMEOUT |
400 | Code node {node} was stopped after {seconds} s |
CODE_MEMORY |
400 | Code node {node} ran out of memory (limit {limit}) |
CODE_RESULT_NOT_JSON |
400 | Code node {node}: main() returned {type}, which cannot be stored as JSON |
CODE_TOO_LARGE |
413 | Code node {node}: the {what} is larger than {max_kb} KB |
CODE_LANGUAGE_UNSUPPORTED |
400 | Code node {node}: {language} is not available on this server |
CODE_WRITE_FORBIDDEN |
403 | Your role may not add Code nodes or change their code (code.write) |
AUTO_APPROVAL_FORBIDDEN |
403 | Only the workflow owner or an admin can turn on automatic AI approval |
AUTO_APPROVAL_DISABLED |
409 | AI auto-approval is turned off on this server |
RUN_NOT_WAITING |
409 | This run is not waiting for a response |
CHECKPOINT_NOT_FOUND |
404 | Checkpoint not found in this run, or it cannot be forked |
RUN_FORK_UNSUPPORTED |
400 | Runs of imported LangGraph graphs cannot be forked |
RUN_RESPONSE_INVALID |
400 | Invalid response type or size (max {max} characters) |
MCP_SERVER_DISABLED |
403 | This workflow is not exposed as an MCP tool; turn it on under API in the editor |
TRIGGER_TOKEN_INVALID |
401 | Missing, invalid or revoked workflow token |
TOKEN_NOT_FOUND |
404 | Token not found |
SCHEDULE_NOT_FOUND |
404 | Schedule not found |
SCHEDULE_CRON_INVALID |
400 | Schedule: |
SCHEDULE_LIMIT |
400 | A workflow can have at most {max} schedules |
CHAT_TRIGGER_NOT_FOUND |
404 | Chat trigger not found |
CHAT_TRIGGER_COMMAND_TAKEN |
409 | Another chat trigger already answers '{command}' there |
CHAT_TRIGGER_LIMIT |
400 | A workflow can have at most {max} chat triggers |
CHAT_TRIGGER_CONFIG_INVALID |
400 | Chat trigger: invalid |
CHAT_TRIGGER_SCOPE_MISSING |
400 | The Slack app lacks the scopes chat triggers need: {scopes}. Reinstall it with them |
CHAT_IDENTITY_UNKNOWN |
403 | Their {kind} account is not linked to Superstep |
CHAT_IDENTITY_UNVERIFIED |
403 | Their {kind} account is linked but not verified |
CHAT_FORBIDDEN |
403 | They may not run this workflow |
CHAT_TRIGGER_OFF |
409 | The chat trigger is off |
CHAT_NOT_ENABLED |
409 | Chat is off for this workflow. Turn it on in the Chat settings |
CHAT_SETTINGS_INVALID |
400 | Chat settings: invalid |
CHAT_APP_KEY_LIMIT |
409 | A chat can have at most two active app keys |
CHAT_APP_KEY_NOT_FOUND |
404 | Chat app key not found |
CHAT_TOKEN_REQUIRED |
401 | A third-party chat user token is required |
CHAT_TOKEN_INVALID |
401 | Invalid third-party chat user token |
CHAT_TOKEN_EXPIRED |
401 | The third-party chat user token has expired |
CHAT_APP_KEY_REVOKED |
401 | This chat app key has been revoked |
CHAT_ORIGIN_FORBIDDEN |
403 | This origin is not allowed for the chat app key |
CHAT_ACCESS_NOT_AVAILABLE |
400 | Chat access '{access}' is not available yet |
CHAT_SESSION_NOT_FOUND |
404 | Chat not found |
CHAT_MESSAGE_EMPTY |
400 | Write a message first |
CHAT_MESSAGE_TOO_LONG |
400 | The message is longer than {max} characters |
CHAT_RUN_WAITING |
409 | Answer the question in the chat first |
CHAT_ANSWER_BY_REVIEWER |
409 | A reviewer answers this step in the Approvals inbox |
CHAT_NOT_PUBLISHED |
404 | This chat is not available |
CHAT_SLUG_INVALID |
400 | The chat address must be 3 to 64 lowercase letters, digits or hyphens |
CHAT_SLUG_TAKEN |
409 | Another chat already uses the address '{slug}' |
CHAT_SIGN_IN_REQUIRED |
401 | Sign in to use this chat |
CHAT_DAILY_LIMIT |
429 | This chat has used its messages for today. Try again tomorrow |
CHAT_EMBED_ORIGIN_INVALID |
400 | '{origin}' is not a site address like https://example.com |
CHAT_LOGO_INVALID |
400 | The logo must be a PNG, JPEG or WebP image of at most {max_kb} KB |
CHAT_LOGO_NOT_FOUND |
404 | This chat has no logo |
CHAT_ATTACHMENTS_OFF |
403 | This chat does not accept attachments |
CHAT_ATTACHMENT_TYPE |
400 | This file type is not allowed here. Allowed: |
CHAT_ATTACHMENT_TOO_LARGE |
413 | A file may be at most {max_mb} MB |
CHAT_ATTACHMENT_TOO_MANY |
400 | A message may carry at most {max} files |
CHAT_ATTACHMENT_NOT_FOUND |
404 | File not found |
CHAT_ATTACHMENT_DEMO |
403 | Attachments are turned off in the public demo |
CHAT_ATTACHMENT_STORAGE |
503 | Attachment storage is not available: |
ATTACHMENT_MIGRATE_TARGET |
400 | Unknown attachment storage '{target}' (use {allowed}) |
ATTACHMENT_MIGRATE_BATCH |
400 | The batch size must be between 1 and |
CHAT_ATTACHMENT_LIMIT |
400 | Attachment limits may be at most {max_files} files and {max_mb} MB per file |
CHAT_REGENERATE_INVALID |
400 | Only the latest completed answer can be regenerated |
CHAT_REGENERATE_UNSUPPORTED |
400 | This server does not support regenerating chat answers |
CHAT_EDIT_INVALID |
400 | This message cannot be edited |
CHAT_EDIT_CONTEXT_UNAVAILABLE |
409 | The agent context at this point is no longer available; start a new conversation |
CHAT_EDIT_BUSY |
409 | Wait for the conversation to finish before creating a new branch |
CHAT_EDIT_STALE |
409 | The conversation changed; refresh it before trying again |
CHAT_EDIT_CONFLICT |
409 | This operation ID was already used for a different revision |
CHAT_EDIT_GONE |
410 | The new conversation branch was removed |
CHAT_EDIT_PENDING |
409 | This conversation branch has an unfinished operation |
CHAT_EDIT_STARTED |
409 | This operation was already admitted; read its saved outcome instead of resending |
CHAT_EDIT_UNSUPPORTED |
400 | This server does not support editing chat messages |
CHAT_TURN_NOT_FOUND |
404 | Message not found |
CHAT_FRAME_INVALID |
400 | Invalid chat frame: |
CHAT_RATING_NOT_ALLOWED |
409 | This message cannot be rated |
CHAT_RATING_INVALID |
400 | Rating: invalid |
TEST_NOT_FOUND |
404 | Test case not found |
TEST_CASE_INVALID |
400 | Test case: |
TEST_CASES_LIMIT |
400 | A workflow can have at most {max} test cases |
TEST_SUITE_NOT_FOUND |
404 | Test run not found |
TEST_SUITE_RUNNING |
409 | The tests of this workflow are already running |
TEST_NOT_SUPPORTED |
400 | Imported LangGraph workflows cannot have tests |
PYTHON_EXPORT_UNAVAILABLE |
400 | Imported LangGraph workflows are Python code already: there is nothing to export |
TYPESCRIPT_EXPORT_UNAVAILABLE |
400 | Imported LangGraph workflows are Python code: they cannot be exported as TypeScript |
EXPORT_LANGUAGE_UNSUPPORTED |
400 | Cannot export to {language}: use one of {supported} |
CALLBACK_URL_INVALID |
400 | callback_url must be a public http(s) URL |
VERSION_NOT_FOUND |
404 | Workflow version not found |
RUN_INTERRUPTED |
500 | The server stopped while this run was going {attempts} times, so it was not continued again |
RUN_EXPIRED |
410 | This run waited too long for a response and expired |
RATE_LIMITED |
429 | Too many requests, retry in {retry_after} s |
NOTIFICATION_NOT_FOUND |
404 | Notification not found |
DELIVERY_NOT_FOUND |
404 | Webhook delivery not found |
NOTIFICATION_PREFS_INVALID |
422 | Preferences: in_app and email must be booleans, lang must be en or vi |
CHANNEL_KIND_UNSUPPORTED |
400 | Unsupported approval channel: |
CHANNEL_CONFIG_INVALID |
400 | Channel setting {field} is missing or invalid |
CHANNEL_NOT_FOUND |
404 | Approval channel not configured |
CHANNEL_ADMIN_REQUIRED |
403 | Only an admin can manage org-wide approval channels |
CHANNEL_PUBLIC_URL_REQUIRED |
400 | Set STUDIO_PUBLIC_URL to the public https URL of the studio first |
CHANNEL_SIGNATURE_INVALID |
401 | Invalid channel request signature |
CHANNEL_API_FAILED |
502 | The {kind} API call failed: {reason} |
IDENTITY_INVALID |
400 | Invalid {kind} user id |
IDENTITY_TAKEN |
409 | This {kind} account is already linked to another user |
IDENTITY_NOT_LINKED |
404 | Link your {kind} account first |
IDENTITY_CODE_INVALID |
400 | Wrong code, {left} attempt(s) left |
IDENTITY_CODE_EXPIRED |
410 | The code expired, request a new one |
IDENTITY_CODE_ATTEMPTS |
429 | Too many wrong codes, request a new one |
APPROVAL_TOKEN_INVALID |
400 | Invalid approval button |
APPROVAL_TOKEN_EXPIRED |
410 | This approval request has expired |
APPROVAL_ALREADY_DECIDED |
409 | This request was already answered |
APPROVAL_IDENTITY_UNKNOWN |
403 | Link and verify your {kind} account in Superstep (Settings → My accounts) to approve |
APPROVAL_FORBIDDEN |
403 | You are not allowed to approve this run |
Codes used only in WebSocket events (RUN_*, NODE_FAILED) list their nominal status for reference.
LLM providers (TASK-0030)¶
PROVIDER_* and MODEL_NOT_FOUND come from POST /api/providers/{id}/test, GET /api/credentials/{id}/models and, as the code of a run error event, from LLM nodes. PROVIDER_AUTH_FAILED is deliberately 400 (not 401) so the UI does not treat a rejected provider key as an expired studio session. PROVIDER_UNREACHABLE in Docker usually means the base URL says localhost: use http://host.docker.internal:<port> (see the handbook, LLM providers).
PROVIDER_EMBEDDINGS_UNSUPPORTED and KB_EMBEDDING_DIMENSION_MISMATCH come from knowledge-base uploads, re-index, search and POST /api/credentials/{id}/embeddings/test. A knowledge base stores the dimension of its vectors (dims); a model that suddenly returns another size (same name, e.g. a re-pulled local model) is refused instead of mixing vectors. Changing the model or credential still re-indexes automatically.
Send email nodes (TASK-0051)¶
CREDENTIAL_SETTINGS_INVALID comes from creating, editing or testing an SMTP credential (details.field names the setting). EMAIL_HOST_BLOCKED, EMAIL_AUTH_FAILED and EMAIL_SEND_FAILED come from its Test connection and, as the code of a run error event, from Send email nodes, together with the other EMAIL_* codes. EMAIL_AUTH_FAILED is 400 (not 401) for the same reason as PROVIDER_AUTH_FAILED. EMAIL_RATE_LIMITED counts recipients per workflow owner in a fixed one-hour window (STUDIO_EMAIL_NODE_HOURLY_LIMIT, default 100).
Code mode of Tool nodes (TASK-0053)¶
The CODE_* codes are the code of a run error event from a Tool node in Code mode. CODE_FAILED, CODE_INVALID (raised by the code itself), CODE_RESULT_NOT_JSON, CODE_TIMEOUT and CODE_MEMORY events also carry code_output with stdout, traceback and runtime. The DEMO_* codes appear only with STUDIO_DEMO_MODE=true (the public demo, docs/deploy/demo.md). CODE_RUNTIME_OFF and CODE_RUNTIME_UNAVAILABLE mean the server cannot run code at all: the mode is off (STUDIO_ENABLE_CODE_NODES), Docker is not reachable, or the image (STUDIO_CODE_IMAGE, default studio-code:1) is missing. TypeScript nodes (TASK-0080) use their own image (STUDIO_CODE_TS_IMAGE, default studio-code-ts:1) or, on the subprocess runtime, the deno binary (STUDIO_DENO); a TypeScript import outside STUDIO_CODE_TS_PACKAGES, or one that is not in Deno's cache, is CODE_INVALID. The allowed entries are pinned name@major (default npm:zod@3,npm:date-fns@4) and an import must name the same major (npm:zod@3, npm:zod@3.25.1 or npm:zod@3/mini); an unversioned npm:zod or another major is refused at save time with a reason that names the allowed spec (import "npm:zod" needs a version: use "npm:zod@3"), and an action Deno has no permission for (files, network, environment, processes) is CODE_FAILED.
CODE_LANGUAGE_UNSUPPORTED (TASK-0079) means the node's data.language is unknown or not in STUDIO_CODE_LANGUAGES on this server (python by default). Nodes without a language are Python. It is raised before anything runs, by a run and by Test with sample state; saving such a graph reports the same problem as a validation error.
CODE_WRITE_FORBIDDEN (TASK-0054) comes from saving a workflow, restoring a version, using or importing a template, and any other write that adds a Code node or changes its code, when the user's role lacks code.write (admins only by default). Running, moving, renaming or deleting a Code node, and duplicating a workflow, stay allowed. Nothing is stored when it is refused.
AUTO_APPROVAL_FORBIDDEN (TASK-0186, REQ-0081) comes from saving a workflow, restoring a version or any other write that turns a node's AI review to auto or changes an auto policy, when the caller is neither the workflow owner nor an admin. Editors may save suggest. Nothing is stored when it is refused. AUTO_APPROVAL_DISABLED comes from the AI review endpoints when the operator turned the feature off (STUDIO_AUTO_APPROVAL=off).