Testing & Debugging
A tight iteration loop catches problems before your users do.
Fix errors as you type
The editor checks your code continuously. Errors are underlined in place, with a message on hover — an unknown function, a wrong argument count, a member access on a value that isn't an object, a type mismatch. Don't publish with errors showing. The graph also won't render flows that don't compile, which is a quick visual signal something's off.
Log with debug.log
Drop a log step anywhere to record a value while you're developing:
log(level: "info", message: `cart total is ${flow.total}`)
Use it to confirm a value is what you expect before a tricky branch. Keep messages short and remove noisy logs before publishing.
Validate before publishing
Before you click Publish, scan for the common mistakes:
- Every inline button's
callback_datahas a matchingflow ... on callback "...". - Every blocking
ask.*handles"timeout". - Every
http.*call has anon error(or useshttp.retry). - Secrets are referenced as
global.*, not hardcoded. - List views handle the empty case.
See the full Production checklist.
A good iteration loop
- Make one change in the editor.
- Read the inline errors — fix anything underlined.
- Glance at the graph — does the flow's shape match your intent?
- Publish and try it in Telegram with a real
/start. - Repeat. Small steps make it obvious which change broke something.
Reproducing edge cases
When testing interactive flows:
- Trigger the timeout path by simply not replying to an
ask.*. - Test the empty state by clearing the relevant
shared.*/user.*variable in the Variables panel. - Test error paths by temporarily pointing an HTTP call at a bad URL, or leaving an API key blank.
Common things to recheck
| Symptom | Likely cause |
|---|---|
| Button does nothing | Missing on callback flow for its callback_data |
| Flow hangs forever | ask.* with no timeout / no "timeout" arm |
Text shows ${name} literally | You used ${} outside a backtick template, or {{}} |
| "Variable not found" at runtime | Reading a scope you never declared, or wrong scope prefix |
| Webhook rejected (401) | Verification secret missing/blank in the Variables panel |
More in Common errors and Gotchas.