Tencent/WeKnora · error
access to system column '%s' is not allowed
Error message
access to system column '%s' is not allowed
What it means
The identifier validator rejected a column reference to a PostgreSQL system column (xmin, xmax, cmin, cmax, ctid, tableoid). These columns expose internal row versioning and physical location metadata, which can be used for row-level probing and break portability, so they are blocked in injected expressions. Comparison is case-insensitive against the lowercased identifier.
Source
Thrown at internal/utils/inject.go:2346
return nil
}
// validateColumnRef validates a column reference
func (v *sqlValidator) validateColumnRef(cr *pg_query.ColumnRef) error {
if !v.checkSystemColumns {
return nil
}
// Check for system column access
for _, field := range cr.Fields {
if s := field.GetString_(); s != nil {
colName := strings.ToLower(s.Sval)
// Block access to system columns
systemColumns := []string{"xmin", "xmax", "cmin", "cmax", "ctid", "tableoid"}
for _, sysCol := range systemColumns {
if colName == sysCol {
return fmt.Errorf("access to system column '%s' is not allowed", colName)
}
}
// Block pg_ prefixed identifiers
if strings.HasPrefix(colName, "pg_") {
return fmt.Errorf("access to '%s' is not allowed", colName)
}
}
}
return nil
}
// getTypeName extracts the type name from a TypeName node
func (v *sqlValidator) getTypeName(tn *pg_query.TypeName) string {
var parts []string
for _, name := range tn.Names {
if s := name.GetString_(); s != nil {
parts = append(parts, s.Sval)
}View on GitHub (pinned to 988cbb0330)
Solutions
- Remove the system column reference and use an explicit version/timestamp column maintained by the application
- Implement optimistic locking with an app-level updated_at/revision column instead of xmin
- Use the database's native API (e.g. direct SQL session, not the injection validator) if system column access is truly required for DBA work
- If filtering duplicates, use DISTINCT or an allowlisted unique key rather than ctid
Example fix
// before filter := "xmin > 100" // after filter := "revision > 100" // app-managed version column
Defensive patterns
Strategy: validation
Validate before calling
var systemColumns = []string{"xmin", "xmax", "cmin", "cmax", "ctid", "tableoid"}
func referencesSystemColumn(col string) bool {
c := strings.ToLower(col)
return slices.Contains(systemColumns, c)
} Type guard
func isSystemColumn(name string) bool {
switch strings.ToLower(name) {
case "xmin", "xmax", "cmin", "cmax", "ctid", "tableoid":
return true
}
return false
} Try / catch
if err := injector.Validate(expr); err != nil {
if strings.Contains(err.Error(), "system column") {
http.Error(w, "system columns cannot be used in filters; use an application-managed version column", http.StatusBadRequest)
return
}
return
} Prevention
- Use app-managed revision/updated_at columns for optimistic concurrency instead of xmin
- Document that MVCC internals are off-limits in user-facing expressions
- Checklist-review migrated Postgres queries for system column usage before enabling them in filters
When it happens
Trigger: An expression referencing a system column such as SELECT xmin FROM t via the validator, or a WHERE/filter string like "xmin::text LIKE '%'", passed as a field identifier node with Sval matching one of the six system column names.
Common situations: Porting legacy Postgres queries that use xmin for optimistic concurrency or change detection; MVCC debugging queries copied into user-facing filters; attackers enumerating row visibility via ctid.
Related errors
- access to '%s' is not allowed
- function '%s' is not allowed (dangerous prefix)
- function '%s' is not allowed
- function not allowed: %s
- join request not found
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/8705a87b01e7660f.
Report an issue: GitHub.