zudo-slack-wisdom
GitHub repository

Type to search...

to open search from anywhere

Errors Reference

Branchable, category-driven error codes across the Lists API, with causes, context, and a production handling policy

Overview

slackLists.items.update and Lists' other write and read methods all return {"ok": false, "error": "<code>"} on failure. This page groups the documented error codes — across every slackLists.* method used elsewhere in this section, not just items.update — by cause, so a Worker can branch on body.error instead of treating every non-ok response the same way. See "Production Error Handling" below for how to turn that branching into an actual retry/halt/alert policy.

The Full Table

ErrorMeaning
invalid_option_idWrong slug — either a label was written instead of a value, or the option was deleted from the schema after you cached it
invalid_input_typeWrong value key for the column type, or a bare scalar where an array was required
invalid_array_argAn array argument was mangled — usually a form-encoded body instead of JSON
invalid_column_id / column_not_foundStale persisted column_id
invalid_row_id / row_not_foundStale persisted row_id (from items.update)
record_not_found / record_deleteditems.info's row-missing spellings — same stale-row meaning as row_not_found / invalid_row_id, different method, different wording
duplicated_item_not_founditems.create's duplicate-from-row source could not be found
column_id_not_providedA cell is missing column_id (and did not supply the undocumented column_id_to_create)
uneditable_columnTarget is a computed column (created_by, last_edited_by, created_time, last_edited_time)
over_cell_fields_limitToo many cells in one call (docs JSON: maxItems: 100)
over_row_maximumPer-list item cap reached (create path; the cap's only authoritative signal)
list_item_limit_exceeded / list_row_limit_exceeded / too_many_items / too_many_recordsAdditional capacity-error spellings surfaced by the source research; unconfirmed — see the taxonomy note below
list_not_foundBad list_id — also reported for access failures, see below
no_permission / access_denied / permission_deniedBot token lacks access to this list
team_access_not_grantedToken lacks the specific workspace access required — appears across most slackLists.* methods, not just items.update
paid_teams_onlyWorkspace is on a free plan — Lists is a paid-plan feature
lists_disabled_user_teamAn admin has disabled Lists for the workspace (distinct from the plan gate above)
archive_not_supporteditems.list's plan-tier error for the archived-items query — archive filtering isn't available on the caller's plan
missing_scopeToken lacks the lists:write scope
not_allowed_token_typeWrong token type for this method (e.g. a user token where a bot token is required, or vice versa)
not_authed / invalid_auth / token_expired / token_revokedThe broader token-auth family alongside not_allowed_token_type — no token present, token fails validation, token expired, or token was revoked
invalid_blocks / invalid_text_blockMalformed rich_text cell on a text/notes column
ratelimitedRate limit hit — honor the Retry-After header

Select-Column Errors

invalid_option_id is the error you will see most often once a select column is in production use. It fires in two distinct situations that look identical on the wire:

  • The value sent is a label, not a value slug — see Select Columns for why labels are never accepted.

  • The option used to exist and was deleted by a human editing the list schema in the UI — see Schema Mutability. A cached slug map does not know this happened; re-read list_metadata.schema via items.info when this error is unexpected.

invalid_input_type and invalid_array_arg are both shape errors rather than value errors — see Writing List Items for the typed-value-key table and why almost every value is array-wrapped even for a single value.

Stale-ID Errors

invalid_column_id / column_not_found and invalid_row_id / row_not_found both mean a persisted ID no longer resolves. items.info reports the same row-missing condition under its own spellings, record_not_found and record_deleted — same cause, different method, different wording, so a code-matching branch has to check both families to catch a stale row id regardless of which method surfaced it. items.create's duplicated_item_not_found is the same shape of problem one level removed: the row you asked to duplicate from is itself gone.

Since there is no slackLists.info or slackLists.list to re-discover a list's structure, and no "find row by external key" endpoint, the only recovery for a stale row id is a full paginated read of the list to rebuild the mapping from whatever your own primary key is stored in (for example, a text column mirroring the source row's ID). A stale column_id means the list's schema changed out from under you — re-read list_metadata.schema via items.info.

Permission and Access Errors

list_not_found is overloaded: it is the generic "bad list_id" error, but it is also what items.update returns when the token's holder lacks access to an otherwise-valid list — there is a community report (slackapi/deno-slack-sdk#472) of exactly this happening despite the list being shared into the bot's channel. Do not assume list_not_found always means a typo'd or deleted ID; it can also mean an access problem. See Select Columns for why bot-creates-the-list sidesteps the access question rather than needing to disambiguate this error.

no_permission, access_denied, and permission_denied are the more direct access-denied family, distinct from the plan-gate and scope errors below. team_access_not_granted is the same family under a different name — it shows up across most slackLists.* methods, not just items.update, whenever the token's workspace-level access doesn't cover this list.

Because list_not_found can mean either "bad ID" or "access problem" and there is no way to tell which from the wire, the category taxonomy below gives it its own category — access_or_stale_id — rather than folding it into either the stale-ID group above or the access-denied family here.

Plan and Scope Errors

Four errors gate on different things and are easy to conflate:

  • paid_teams_only — the workspace itself is on a free plan; Lists requires a paid plan.

  • lists_disabled_user_team — the workspace is paid, but a workspace admin has turned Lists off. This is an admin toggle, not a billing state, and both errors can look identical from inside a Worker's error-handling branch unless you check for both explicitly.

  • archive_not_supporteditems.list's archived-items query isn't available on the caller's plan; a third, narrower plan gate distinct from the two above.

  • missing_scope — the token itself lacks lists:write. Unlike the errors above, this is fixable by reinstalling the app with the correct scope, not by anything the workspace admin or plan tier controls.

Rich Text Errors

invalid_blocks and invalid_text_block both mean a text/notes column received something other than a valid Block Kit rich_text structure — most commonly a plain string, which text columns never accept on the write path (see Writing List Items).

Rate Limiting

slackLists.items.update sits on Tier 3 (50+ requests/min), per both the docs and the Java SDK's machine-readable rate_limit_tiers.json — the two sources agree for this method. ratelimited carries a Retry-After header stating how many seconds to wait before retrying; honor it rather than retrying immediately or on a fixed interval.

Production Error Handling: Branch on Category, Not Code

A Worker that branches on the literal error string ends up re-deriving the same handful of policies over and over, once per code. It's more robust to map every code into a small category taxonomy first, then drive retry/halt/alert policy off the category — new codes Slack adds later slot into an existing bucket instead of falling through to a default.

CategoryCodesPolicy
scopemissing_scopePermanent configuration error
authnot_allowed_token_type, not_authed, invalid_auth, token_expired, token_revokedPermanent configuration error
accessno_permission, access_denied, permission_denied, team_access_not_grantedPermanent configuration error
access_or_stale_idlist_not_foundPermanent configuration error
schemainvalid_option_id, invalid_column_id / column_not_found, uneditable_column, column_id_not_provided, invalid_input_type, invalid_array_arg, invalid_blocks / invalid_text_blockPermanent configuration error
planpaid_teams_only, lists_disabled_user_team, archive_not_supportedPermanent configuration error
capacityover_row_maximum, over_cell_fields_limit, and the unconfirmed list_item_limit_exceeded / list_row_limit_exceeded / too_many_items / too_many_records spellingsPermanent configuration error (a ceiling/design fix, not a retry — see Item Caps and Auto-Archive)
stale_idinvalid_row_id / row_not_found, record_not_found / record_deleted, duplicated_item_not_foundFeed reconciliation
transientinternal_error, fatal_error, service_unavailable, request_timeoutBackoff
rate_limitratelimitedBackoff

The first six rows — scope, auth, access, access_or_stale_id, schema, plan — plus capacity are all permanent configuration errors: retrying cannot fix them, because nothing about the request changes on the next attempt. On any code in these categories, mark the List registration unhealthy, halt sync against that list, and alert — a human (or a schema-drift redeploy, see Schema Mutability) has to change something before sync can resume safely.

transient and rate_limit are the opposite: back off and retry. ratelimited carries a Retry-After header telling you how long; the generic server-error family (internal_error, fatal_error, service_unavailable, request_timeout) doesn't, so use a capped exponential backoff instead.

stale_id is neither — it isn't a config problem to halt on, nor pure noise to retry through. It means your locally persisted ID no longer resolves, which is exactly the signal a reconcile pass exists to correct: feed it into the next reconcile cycle to rebuild the mapping rather than either halting the whole sync or blindly retrying the same stale ID.

Sanitize error codes before logging or echoing

body.error is response data that reflects whatever request you sent — treat it as attacker-influenceable rather than a trusted enum, especially before writing it into a log line, an alert payload, or anything an operator might view unescaped. Validate it against^[a-z0-9_]{1,80}$ before logging or echoing it anywhere; fall back to a generic "unrecognized error" label for anything that doesn't match rather than passing the raw string through.

For the request/response contract these errors apply to, see Writing List Items. For the select-value rules behind invalid_option_id, see Select Columns. For why the schema itself is closed to most API-driven changes, see Schema Mutability. For what the capacity error over_row_maximum means and how to avoid triggering it in normal operation, see Item Caps and Auto-Archive.

Revision History

CreatedUpdated