Skip to main content

6 Slot Mistakes That Break in Production

  • March 19, 2026
  • 1 reply
  • 68 views

Kevin Mok
Forum|alt.badge.img+1

Hey builders πŸ‘‹

I've been reviewing a lot of plugins lately, and the same slot mistakes keep showing up. They all pass in testing. They all break in production. So I wrote up the six patterns I see most often, with the bad config and the fix for each.

Β 

🚨 1. Using string for people

The #1 slot mistake I see. You're asking the engine to parse emails from free text.

# Bad β€” forces the engine to parse structured data from free text
- name: attendees
description: "Comma-separated list of attendee emails like john@company.com, jane@company.com"
data_type: string

User says "invite John and the marketing team." Your string slot captures the literal string "John and the marketing team". The engine tries to parse that into emails. Fails. User tries "john@company.com, marketing-team@company.com". Engine parses it. But marketing-team is a distribution list, not a user. API call fails with "Invalid attendee".

# Good β€” platform handles fuzzy matching, disambiguation, validation
- name: attendees
description: "People to invite to the meeting"
data_type: list[User]

The platform's User resolver fuzzy-matches "John" to john.smith@company.com, shows a disambiguation picker if there are multiple Johns, and validates users exist before proceeding. If you're collecting people, use User or list[User]. Always.

Β 

🚨 2. Stuffing rules into descriptions

Slot descriptions tell the engine what a slot represents. They're semantic labels, not rule engines. I keep seeing descriptions with 5+ lines of validation logic.

# Bad β€” none of this gets enforced
- name: due_date
description: |
The date when the task is due. MUST be in YYYY-MM-DD format.
The date CANNOT be in the past. If user provides a past date,
reject it and ask again. The date should be within the next
90 days. If the user says "tomorrow", calculate the date.
data_type: string

User submits "yesterday." The engine sees the rule in the description but has no enforcement mechanism. Bad data hits your API, which returns something like "Invalid date range". User sees a generic failure. Support ticket filed.

# Good β€” description says what, policy enforces how
- name: due_date
description: "The target completion date for this task. Expected format: YYYY-MM-DD."
data_type: string
validation_policy:
rule: $PARSE_TIME(value) >= $TIME()
error_message: "Due date must be today or in the future"

Format hints like "Expected format: YYYY-MM-DD" in descriptions are fine. They help the engine collect the right shape of data. What doesn't belong is behavioral logic: "if the user says X, do Y" or "reject and ask again." Move those to validation policies.

Β 

🚨 3. No validation policy on free text

Without a validation policy, whatever the user provides passes through. "Priority 99" passes through. "Start date: last Tuesday" passes through. Your API receives garbage, returns errors, and the user sees a generic failure message.

# Bad β€” the "1-5" constraint in the description is a suggestion, not a rule
- name: priority
description: "Priority level 1-5 where 1 is highest"
data_type: number

HR system only accepts priority 1-5. User says "make it priority 10 β€” super urgent!" Engine passes priority: 10. API returns 400 Bad Request. User sees "Something went wrong."

# Good β€” validation catches bad input, gives the user a clear message
- name: priority
description: "Priority level"
data_type: number
validation_policy:
rule: value >= 1 AND value <= 5
error_message: "Priority must be between 1 (highest) and 5 (lowest)"

When validation fails, the engine shows the error_message and re-collects the slot automatically. The user gets a clear, specific prompt instead of a cryptic API error.

Β 

Quick reference for common validation patterns:

Use Case DSL Expression
Date not in past $PARSE_TIME(value) >= $TIME()
Number in range value >= 1 AND value <= 100
Allowed values value IN ['low', 'medium', 'high']

Β 

🚨 4. Raw strings for timezones

Users say "PST" or "Pacific Time." APIs need America/Los_Angeles. Without a resolver mapping display values to IANA identifiers, the raw user input flows straight to your API.

# Bad β€” user enters "PST", API receives "PST", API doesn't recognize "PST"
- name: timezone
description: "User's timezone like PST, EST, or America/Los_Angeles"
data_type: string

Meeting scheduled for "9 AM PST." MS Graph Calendar API receives timeZone: "PST". Graph needs IANA format. Meeting created with the wrong timezone. Attendees in different timezones see the wrong time. Everyone shows up at different times.

# Good β€” static resolver maps display values to what the API actually needs
- name: timezone
description: "Timezone for the meeting in IANA format"
data_type: string
resolver_strategy:
type: static
options:
- display_value: "Pacific Time (PT)"
value: "America/Los_Angeles"
- display_value: "Eastern Time (ET)"
value: "America/New_York"
- display_value: "Central Time (CT)"
value: "America/Chicago"
- display_value: "Mountain Time (MT)"
value: "America/Denver"

User sees "Pacific Time (PT)" in the selection. Slot stores "America/Los_Angeles". API gets the right format. Yes, static resolvers add an LLM call. For timezone mapping, it's worth it.

Β 

🚨 5. Static resolver for yes/no choices

Static resolvers add an LLM call to resolve the user's input. For binary yes/no choices, that's ~200-500ms of extra latency and extra cost with zero benefit over a native boolean slot.

# Bad β€” resolver adds an LLM call for a two-option choice
- name: meeting_type
description: "Either virtual or in-person"
data_type: string
resolver_strategy:
type: static
options:
- display_value: "Virtual (Teams)"
value: "virtual"
- display_value: "In-Person"
value: "in-person"

Every meeting request takes 500ms longer than necessary. Multiply by thousands of requests. Your plugin is "slow" compared to others. Users notice.

# Good β€” boolean slot, no resolver, no extra LLM call
- name: is_virtual
description: "Whether this is a virtual meeting with a Teams link"
data_type: boolean

If it's a two-option choice (approve/reject, yes/no, virtual/in-person), use boolean. Save the LLM call. Use resolvers when display values differ from API values, when you have more than 2 options, or when you need fuzzy matching on input.

Β 

🚨 6. "Required if..." in a slot description

You can't conditionally collect one slot based on another slot's value through slot configuration alone. The engine ignores "Required if meeting_type is in-person" comments in descriptions. It either always collects the slot or never does.

# Bad β€” the engine ignores the "Required if" comment entirely
- name: room_name
description: "Conference room for in-person meetings. Required if meeting_type is in-person."
data_type: string

User schedules a virtual meeting. Engine asks "What conference room?" User is confused β€” "It's virtual, I don't need a room." User types "none" or "N/A." Your API receives room: "none". Room booking system tries to book a room named "none."

# Good β€” decision policy controls flow based on collected values
decision_policies:
- name: collect_room_if_in_person
condition: data.is_virtual == false
action: goto_activity:collect_room
else_action: goto_activity:create_meeting

Use a decision policy in your conversation process to route based on collected slot values. Users scheduling virtual meetings never see a room prompt. If one slot's resolver options depend on another slot's value (like fetching subcategories based on a selected category), that's a different problem β€” use context passing in your resolver strategy.

Β 

I published a full guide with all of this plus a quick reference table:

πŸ‘‰ Slot Best Practices

If you've hit a slot pattern that burned you that I didn't cover, drop it below. I'll add it to the guide.

1 reply

JenHanley
Forum|alt.badge.img+1
  • Community Manager
  • March 26, 2026

This is a great post, thanks for sharing!