ErrLookup › Background articles › Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list
Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list
Invalid enum value errors ("Unknown worker type", "Invalid scope", "X must be one of ...", "is not a valid mode") fire when a string argument, configuration value, or stored field does not exactly match one of the closed set of literals a library accepts. Developers meet this family on typos and case mismatches, on values ported from sibling frameworks (Playwright wait states, sentence-transformers' 'passage'), on unvalidated environment and config overrides, and on database rows written without validation. This article explains the mechanism shared by the 85 documented records across 23 repositories, how the failure surface differs per library, and the remediation themes that hold across the family.
Distilled from 85 documented records across 23 repositories.
Background
Every record in this family comes from the same shape of code: a closed set of literals defined inside the library, and an exact comparison at the point of use. Mechanically it is a switch or match expression whose default arm throws (ruflo's worker and transport types, PhpSpreadsheet's sheetview layouts and explicit datatypes, SiYuan's heading fold scope), a map or registry lookup that misses (PhpSpreadsheet's dynamic-filter date functions, career-ops' template kinds, ruflo's completion modes), or an explicit allowlist test ahead of a write (Rocket.Chat's role scopes and outgoing-integration events, anything-llm's roles, k6's permission names). The set is closed because the literals select behavior, not presentation: chroma's BM25 embedding function dispatches 'document' to corpus indexing and 'query' to a separate path with different term statistics, SiYuan's fold scope chooses between two different transactions, and k6's waitUntil values map to concrete browser lifecycle events. From the caller's side the experience is uniform: a string that looks right is rejected because it is not the exact literal, and the comparison is case-sensitive and whitespace-sensitive almost everywhere in the family.
Direct argument typos are the visible surface, but the records show three deeper channels. Values cross library boundaries carrying the wrong vocabulary: Playwright's 'enabled' and 'stable' wait states are rejected by k6, which supports only 'attached', 'detached', 'visible' and 'hidden'; sentence-transformers' 'passage' is rejected by chroma, which wants 'document'; chat-ui's 'legacy_completions' is rejected by ruflo. Values enter through configuration that never sees the library's validation: environment variables such as TRANSPORT=ws and FOCUS_FREQUENCY=weekly, JSON5 overrides cast to any, CLI flags. And values arrive from storage written without the check: an embed row whose chat_mode was edited directly in the database fails every subsequent request, Mongo user documents with free-text statusConnection warn on every decode, and turso rejects enum members persisted by a release that defined a different set.
Static types do not close the gap. TypeScript's TransportType union stops invalid literals only for checked callers; casts, JavaScript callers and config strings reach the factory and throw at runtime. Python's Literal hint on chroma's task parameter is documentation, not enforcement: __init__ accepts anything and the error fires at call time. A zod schema guards ruflo's endpoint options, but an object passed without schema.parse bypasses it and reaches the final else. Where a guard does exist, the failure can also mean guard drift: appwrite's 'Unknown queue name' 500 appears only when the whitelist that validated the request and the match keys no longer agree, typically because _APP_*_QUEUE_NAME environment values changed after the route table was built.
What happens after the check varies by library, so treat severity as library-specific. Most members reject before any work happens; validation runs ahead of database writes, so nothing is persisted and a corrected retry is safe. Exceptions exist: Rocket.Chat's user codec logs a warning and passes the bad statusConnection through, and career-ops' tracker merge warns and then silently records the row under the wrong canonical state. Message quality also varies: some errors interpolate the full allowed list, so the correction is already in the text; some echo only the rejected value; and neon's scheduling-policy hint omits one accepted literal ('activating') while spelling 'decomissioned' with a single m, matching the code rather than the dictionary.
Common causes
- Typo or near-miss spelling. The most frequent trigger across the records: case mismatch ('Visible', 'PageLayout', 'CV', 'users' for 'Users'), singular vs plural ('notification', 'plugin', 'languages'), hyphens or underscores instead of camelCase ('send-message' for 'sendMessage'), and trailing whitespace. The comparison is exact almost everywhere in the family.
- Vocabulary ported from another library. Values that are valid in a sibling framework are rejected here: Playwright's 'enabled'/'stable' wait states in k6, Chromium raw permission names like 'clipboardReadWrite', sentence-transformers' 'passage' where chroma wants 'document', chat-ui's 'legacy_completions' in ruflo, and the standard English 'decommissioned' where neon matches 'decomissioned' with a single m.
- Unvalidated config, env, or CLI input. Strings read from environment variables (TRANSPORT=ws, FOCUS_FREQUENCY=weekly), config files parsed as JSON5 and cast to any, or CLI flags reach the enum check without passing through the library's own validation. The failure then surfaces at call time or at the first scheduled run, far from where the value was set.
- Persisted data written without validation. A stored field that was never checked at write time fails every checked read: an embed row's chat_mode edited directly in the database returns 400 for every message, Mongo user documents with free-text statusConnection warn on every app decode, and enum members persisted by an older release fail after upgrade.
- Client/server version or fork drift. The literal set is a contract that changes between releases and forks. A client written against a fork with extra roles, an older admin UI sending retired feature names, and PHPExcel-era constant strings all send values the running server no longer accepts.
- Runtime bypass of compile-time types. TypeScript unions and Python Literal hints stop invalid literals only for checked callers. Casts like 'as TransportType', JavaScript callers, 'as any' config objects, and objects passed without schema.parse reach the enum check and fail at runtime.
- Omitted or empty value where a default was assumed. An empty queue-name segment, an omitted integration event field, or a query_config dict lacking its 'task' key (read with .get(), so the error reads 'Invalid task: None') show that several members treat missing as invalid rather than falling back to a default.
What usually fixes it
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
Documented occurrences
- general_server_error: Unknown queue name: {name} (appwrite/appwrite)
- Unknown worker type: ${type} (ruvnet/ruflo)
- The user ${user.username} (${user._id}) does not have a valid status (offline, online, away, or busy). It is currently: "${statusConnection}" (RocketChat/Rocket.Chat)
- Invalid value {value} for enum type {enumType}. (tursodatabase/turso)
- Invalid task: {self.task} (chroma-core/chroma)
- Invalid completion type (ruvnet/ruflo)
- invalid package type (siyuan-note/siyuan)
- invalid dynamic rule type $dynamicRuleType (PHPOffice/PhpSpreadsheet)
- Invalid role. Allowed roles are: ${VALID_ROLES.join(", ")} (Mintplex-Labs/anything-llm)
- Unknown component family: ${family}. Expected one of ${Object.keys(COMPONENT_FAMILY_PREFIXES).join(', ')} (affaan-m/ECC)
- %q is an invalid permission (grafana/k6)
- ${key} must be one of ${values.join(", ")} (JuliusBrussee/caveman)
- bad_request: User feature ${unsupported.join(', ')} is not configurable (toeverything/AFFiNE)
- parsing wait for selector %q options: %w (grafana/k6)
- parsing waitForSelector %q options: %w (grafana/k6)
- error-invalid-scope: Invalid scope (RocketChat/Rocket.Chat)
- Invalid task: {task} (chroma-core/chroma)
- #NUM!: #NUM! (PHPOffice/PhpSpreadsheet)
- ${embed.chat_mode} is not a valid mode. (Mintplex-Labs/anything-llm)
- parsing frame navigation options to %q: %w (grafana/k6)
…and 65 more across the corpus — use search.
Honest provenance: generated on 2026-08-20 from AI-assisted analysis of the linked records. See how records are made.