knadh/listmonk · error
table '%s' is not allowed
Error message
table '%s' is not allowed
What it means
After extracting table names from a custom subscriber query's plan, each table is checked against a whitelist of allowed tables. If any relation in the query is not in allowedTables, this error is returned, preventing arbitrary table access through user-supplied SQL used by QuerySubscribers/ExportSubscribers.
Source
Thrown at internal/core/subscribers.go:626
return err
}
defer tx.Rollback()
var plan string
if err = tx.QueryRow("EXPLAIN (FORMAT JSON) "+query, args...).Scan(&plan); err != nil {
return err
}
// Extract all relation names from the JSON plan.
tables, err := getTablesFromQueryPlan(plan)
if err != nil {
return fmt.Errorf("error getting tables from query: %v", err)
}
// Validate against allowed tables.
for _, table := range tables {
if _, ok := allowedTables[table]; !ok {
return fmt.Errorf("table '%s' is not allowed", table)
}
}
return nil
}
// getTablesFromQueryPlan parses the EXPLAIN JSON to find all "Relation Name" entries.
func getTablesFromQueryPlan(explainJSON string) ([]string, error) {
var plans []map[string]any
if err := json.Unmarshal([]byte(explainJSON), &plans); err != nil {
return nil, err
}
// Collect table names in `tables` recursively.
tables := make(map[string]struct{})
for _, plan := range plans {
traverseQueryPlan(plan, tables)
}View on GitHub (pinned to 670c01717d)
Solutions
- Restrict the query to whitelisted tables: subscribers, lists, subscriber_lists (and their allowed columns)
- Fix table-name typos or use the exact schema-qualified name if the whitelist contains qualified names
- If additional tables are legitimately needed, extend the allowedTables whitelist in the code and redeploy (mind SQL-injection implications)
- Use the library's public list/query APIs instead of raw SQL against unrelated tables
Example fix
// before SELECT * FROM users WHERE email LIKE '%@corp.com' // after SELECT s.* FROM subscribers s WHERE s.email LIKE '%@corp.com'
Defensive patterns
Strategy: validation
Validate before calling
allowed := map[string]bool{"subscribers": true, "lists": true, "subscriber_lists": true}
for _, t := range extractTableNames(query) {
if !allowed[t] {
return fmt.Errorf("table %q not permitted; use subscribers/lists/subscriber_lists", t)
}
} Try / catch
if err := validateQueryTables(query); err != nil {
var denied bool
if strings.Contains(err.Error(), "is not allowed") { denied = true }
return fmt.Errorf("invalid segment query (denied=%v): %w", denied, err)
} Prevention
- Only reference whitelisted tables (subscribers, lists, subscriber_lists) in custom queries
- Double-check table-name spelling against the whitelist before saving a query
- Don't join to application/admin tables in subscriber segments — use the public APIs instead
- If new tables are genuinely needed, update allowedTables in code with a security review
When it happens
Trigger: Passing a query to QuerySubscribers or ExportSubscribers that SELECTs from, JOINs to, or otherwise references any table outside the whitelist — e.g. querying 'users', 'campaigns', or any admin table instead of only subscribers/lists/subscriber_lists.
Common situations: Trying to build segments from tables the library intentionally hides, typos in table names, referencing views or temp tables not on the whitelist, or JOINing to application tables in a custom export.
Related errors
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/db360695c449b5d9.
Report an issue: GitHub.