generalbots/botbook/src/04-basic-scripting/keyword-on-error.md

476 lines
10 KiB
Markdown
Raw Normal View History

# ON ERROR RESUME NEXT
The `ON ERROR RESUME NEXT` keyword enables BASIC error handling, allowing scripts to continue execution when errors occur instead of terminating immediately.
---
## Syntax
```basic
ON ERROR RESUME NEXT ' Enable error trapping
ON ERROR GOTO 0 ' Disable error trapping
```
---
## Description
`ON ERROR RESUME NEXT` enables error trapping mode where execution continues to the next statement after an error occurs, rather than halting the script. This pattern is borrowed from BASIC and provides a simple way to handle errors gracefully in BASIC scripts.
When error trapping is enabled:
- Errors are captured but don't stop execution
- The `ERROR` function returns `TRUE` if an error occurred
- The `ERROR MESSAGE` function returns the error description
- The `ERR` variable contains the error number
Use `ON ERROR GOTO 0` to disable error trapping and restore normal error behavior.
---
## Error State Keywords
| Keyword | Description | Example |
|---------|-------------|---------|
| `ON ERROR RESUME NEXT` | Enable error trapping | `ON ERROR RESUME NEXT` |
| `ON ERROR GOTO 0` | Disable error trapping | `ON ERROR GOTO 0` |
| `ERROR` | Returns TRUE if error occurred | `IF ERROR THEN` |
| `ERROR MESSAGE` | Get last error message | `msg = ERROR MESSAGE` |
| `ERR` | Get error number | `code = ERR` |
| `CLEAR ERROR` | Clear error state | `CLEAR ERROR` |
| `THROW` | Raise a custom error | `THROW "Invalid input"` |
| `ASSERT` | Assert a condition | `ASSERT count > 0, "Count must be positive"` |
---
## Examples
### Basic Error Handling
```basic
' Enable error trapping
ON ERROR RESUME NEXT
' Attempt a risky operation
result = GET "https://api.example.com/data"
' Check if it failed
IF ERROR THEN
TALK "Sorry, I couldn't fetch the data: " + ERROR MESSAGE
ELSE
TALK "Data retrieved successfully!"
END IF
```
### Multiple Operations with Error Checking
```basic
ON ERROR RESUME NEXT
' Try to read a file
content = READ "config.csv"
IF ERROR THEN
TALK "Config file not found, using defaults"
CLEAR ERROR
content = "name,value\ndefault,true"
END IF
' Try to parse it
data = PARSE_CSV(content)
IF ERROR THEN
TALK "Failed to parse config: " + ERROR MESSAGE
CLEAR ERROR
END IF
' Continue with whatever data we have
TALK "Loaded " + LEN(data) + " configuration entries"
```
### Database Operations
```basic
ON ERROR RESUME NEXT
' Try to insert a record
INSERT "customers", #{
"name": customer_name,
"email": customer_email,
"created_at": NOW()
}
IF ERROR THEN
IF INSTR(ERROR MESSAGE, "duplicate") > 0 THEN
TALK "This customer already exists in our system."
ELSE IF INSTR(ERROR MESSAGE, "connection") > 0 THEN
TALK "Database is temporarily unavailable. Please try again."
ELSE
TALK "Could not save customer: " + ERROR MESSAGE
END IF
ELSE
TALK "Customer saved successfully!"
END IF
ON ERROR GOTO 0 ' Disable error trapping
```
### HTTP Request with Fallback
```basic
ON ERROR RESUME NEXT
' Try primary API
data = GET "https://primary-api.example.com/endpoint"
IF ERROR THEN
PRINT "Primary API failed: " + ERROR MESSAGE
CLEAR ERROR
' Try fallback API
data = GET "https://fallback-api.example.com/endpoint"
IF ERROR THEN
TALK "Both APIs are unavailable. Please try again later."
RETURN
END IF
END IF
' Process the data
TALK "Retrieved: " + data.name
```
### File Operations with Cleanup
```basic
ON ERROR RESUME NEXT
' Generate a report
report = GENERATE PDF "templates/report.html", report_data, "temp/report.pdf"
IF ERROR THEN
TALK "Failed to generate report: " + ERROR MESSAGE
CLEAR ERROR
ELSE
' Try to send it
SEND MAIL recipient, "Your Report", "Please find your report attached.", [report.localName]
IF ERROR THEN
TALK "Report generated but email failed: " + ERROR MESSAGE
DOWNLOAD report.url AS "report.pdf"
TALK "You can download it directly instead."
ELSE
TALK "Report sent successfully!"
END IF
' Clean up temp file
DELETE "temp/report.pdf"
' Ignore cleanup errors
CLEAR ERROR
END IF
ON ERROR GOTO 0
```
### Validation with ASSERT
```basic
ON ERROR RESUME NEXT
' Validate input
ASSERT LEN(email) > 0, "Email is required"
IF ERROR THEN
TALK ERROR MESSAGE
RETURN
END IF
ASSERT INSTR(email, "@") > 0, "Invalid email format"
IF ERROR THEN
TALK ERROR MESSAGE
RETURN
END IF
ASSERT amount > 0, "Amount must be positive"
IF ERROR THEN
TALK ERROR MESSAGE
RETURN
END IF
' All validations passed
TALK "Processing your request..."
```
### Custom Error with THROW
```basic
ON ERROR RESUME NEXT
' Check business rules
IF quantity > inventory_count THEN
THROW "Insufficient inventory: only " + inventory_count + " items available"
END IF
IF customer_credit < total_price THEN
THROW "Insufficient credit: need $" + FORMAT(total_price - customer_credit, "#,##0.00") + " more"
END IF
' Check for errors from THROW
IF ERROR THEN
TALK "Cannot process order: " + ERROR MESSAGE
RETURN
END IF
' Continue with order processing
TALK "Order confirmed!"
```
### Loop with Error Recovery
```basic
ON ERROR RESUME NEXT
successful = 0
failed = 0
FOR EACH item IN items_to_process
' Process each item
result = POST "https://api.example.com/process", item
IF ERROR THEN
PRINT "Failed to process item " + item.id + ": " + ERROR MESSAGE
failed = failed + 1
CLEAR ERROR ' Clear error to continue loop
ELSE
successful = successful + 1
END IF
NEXT
ON ERROR GOTO 0
TALK "Processing complete: " + successful + " succeeded, " + failed + " failed"
```
### Nested Error Handling
```basic
ON ERROR RESUME NEXT
' Outer operation
connection = CONNECT "database"
IF ERROR THEN
TALK "Database connection failed"
RETURN
END IF
' Inner operation
ON ERROR RESUME NEXT
result = QUERY connection, "SELECT * FROM users"
IF ERROR THEN
TALK "Query failed, trying alternative..."
CLEAR ERROR
result = QUERY connection, "SELECT * FROM users_backup"
END IF
IF ERROR THEN
TALK "All queries failed: " + ERROR MESSAGE
ELSE
TALK "Found " + LEN(result) + " users"
END IF
ON ERROR GOTO 0
```
---
## Error Codes
Common error codes returned by `ERR`:
| Code | Category | Description |
|------|----------|-------------|
| 1xxx | Network | HTTP and connection errors |
| 2xxx | Database | SQL and data errors |
| 3xxx | File | File system errors |
| 4xxx | Validation | Input validation errors |
| 5xxx | Auth | Authentication/authorization errors |
| 9xxx | System | Internal system errors |
### Checking Error Codes
```basic
ON ERROR RESUME NEXT
result = GET url
IF ERROR THEN
SELECT CASE ERR
CASE 1001
TALK "Network timeout - please try again"
CASE 1002
TALK "Server not found"
CASE 1003
TALK "Connection refused"
CASE 1404
TALK "Resource not found"
CASE 1500
TALK "Server error - please try later"
CASE ELSE
TALK "Error " + ERR + ": " + ERROR MESSAGE
END SELECT
END IF
```
---
## Best Practices
### Always Clear Errors When Continuing
```basic
ON ERROR RESUME NEXT
result = RISKY_OPERATION()
IF ERROR THEN
' Handle the error
TALK "Operation failed"
CLEAR ERROR ' Clear before next operation
END IF
' Next operation won't see stale error
result2 = ANOTHER_OPERATION()
```
### Disable Error Trapping When Done
```basic
ON ERROR RESUME NEXT
' Protected code section
result = RISKY_OPERATION()
IF ERROR THEN
TALK "Handled error: " + ERROR MESSAGE
END IF
ON ERROR GOTO 0 ' Re-enable normal error behavior
' Errors here will halt execution as normal
```
### Log Errors for Debugging
```basic
ON ERROR RESUME NEXT
result = COMPLEX_OPERATION()
IF ERROR THEN
' Log full details for debugging
PRINT "ERROR at " + FORMAT(NOW(), "YYYY-MM-DD HH:mm:ss")
PRINT " Code: " + ERR
PRINT " Message: " + ERROR MESSAGE
PRINT " Context: processing " + current_item.id
' User-friendly message
TALK "Something went wrong. Our team has been notified."
CLEAR ERROR
END IF
```
### Don't Ignore Errors Silently
```basic
' BAD - Errors are silently ignored
ON ERROR RESUME NEXT
data = GET url
TALK "Got data: " + data ' May fail if GET failed
' GOOD - Check and handle errors
ON ERROR RESUME NEXT
data = GET url
IF ERROR THEN
TALK "Could not fetch data"
data = DEFAULT_DATA
END IF
TALK "Got data: " + data
```
### Use Specific Error Checks
```basic
ON ERROR RESUME NEXT
result = DATABASE_OPERATION()
IF ERROR THEN
error_msg = ERROR MESSAGE
' Handle specific errors differently
IF INSTR(error_msg, "timeout") > 0 THEN
WAIT 2
result = DATABASE_OPERATION() ' Retry
ELSE IF INSTR(error_msg, "duplicate") > 0 THEN
TALK "This record already exists"
ELSE IF INSTR(error_msg, "permission") > 0 THEN
TALK "You don't have permission for this action"
ELSE
TALK "Database error: " + error_msg
END IF
END IF
```
---
## Comparison with TRY...CATCH
For more complex error handling, you can use structured `TRY...CATCH` blocks:
```basic
' ON ERROR style (simpler)
ON ERROR RESUME NEXT
result = RISKY()
IF ERROR THEN
TALK "Error: " + ERROR MESSAGE
END IF
ON ERROR GOTO 0
' TRY...CATCH style (more structured)
TRY
result = RISKY()
CATCH e
TALK "Error: " + e.message
END TRY
```
Use `ON ERROR RESUME NEXT` for:
- Simple scripts with few error points
- Quick prototyping
- BASIC-familiar developers
Use `TRY...CATCH` for:
- Complex error handling logic
- Multiple specific catch blocks needed
- Modern structured programming style
---
## Implementation Notes
- Implemented in Rust under `src/basic/keywords/errors/on_error.rs`
- Error state is thread-local per script execution
- Error trapping scope is global to the script (not block-scoped)
- `CLEAR ERROR` resets both `ERROR` flag and `ERROR MESSAGE`
- Maximum error message length: 4096 characters
---
## Related Keywords
- [THROW](keyword-throw.md) — Raise custom errors
- [ASSERT](keyword-assert.md) — Assert conditions
- [PRINT](keyword-print.md) — Debug output for error logging
- [WAIT](keyword-wait.md) — Delay before retry
---
## Summary
`ON ERROR RESUME NEXT` provides BASIC error handling for BASIC scripts, allowing graceful handling of errors without script termination. Always check `ERROR` after risky operations, use `CLEAR ERROR` before subsequent operations, and disable error trapping with `ON ERROR GOTO 0` when protection is no longer needed. For user-facing errors, provide clear messages while logging technical details for debugging.