Tencent/WeKnora · error
failed to parse SQL
Error message
failed to parse SQL: %v
What it means
After validation passes, ValidateAndSecureSQL re-parses the SQL with pg_query.Parse to obtain a parse tree for rewriting (tenant/soft-delete filter injection). If the PostgreSQL grammar parser rejects the SQL, this error wraps the parser message. It indicates the SQL string is not valid PostgreSQL syntax.
Solutions
- Read the wrapped pg_query error to find the syntax position and fix the SQL
- Verify the query is valid PostgreSQL by running it (or EXPLAIN) against psql
- Check the pg_query library version supports the SQL features used
- Confirm the same string is passed to validation and rewrite (no mutation in between)
Example fix
// before q := "SELECT * FROM knowledge_bases LIMIT ?" secured, _, err := utils.ValidateAndSecureSQL(q) // after q := "SELECT * FROM knowledge_bases LIMIT $1" secured, _, err := utils.ValidateAndSecureSQL(q)
Defensive patterns
Strategy: validation
Validate before calling
// Dry-run with the same parser before calling the library
if _, err := pg_query.Parse(sql); err != nil {
return fmt.Errorf("invalid PostgreSQL syntax: %w", err)
} Try / catch
secured, _, err := utils.ValidateAndSecureSQL(sql)
if err != nil && strings.Contains(err.Error(), "failed to parse SQL") {
return fmt.Errorf("invalid PostgreSQL syntax: %w", err)
} Prevention
- Write PostgreSQL-dialect SQL only (use $1 placeholders, not ?)
- Keep the pg_query-go dependency up to date
- Test generated queries against a real PostgreSQL instance in CI
- Never mutate the SQL string between construction and validation
When it happens
Trigger: SQL that passes the library's lightweight validation but cannot be parsed by the actual PostgreSQL parser via pg_query.Parse — e.g. dialect-specific syntax, malformed expressions, or SQL mutated between validation and this call.
Common situations: Using MySQL/SQLite-flavored syntax that PostgreSQL rejects; pg_query library version mismatch producing different grammar behavior; queries with placeholder/param syntax the parser does not accept.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- compound queries (UNION/INTERSECT/EXCEPT) are not allowed
- failed to normalize SQL
- locking clauses (FOR UPDATE, etc.) are not allowed
- SELECT INTO is not allowed
- WITH clause (CTEs) is not allowed
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/4d01d28b64385c19.
Report an issue: GitHub.
Appendix: source
Thrown at internal/utils/inject.go:880
// Find validator config to check if tenant injection is enabled
validator := &sqlValidator{
tablesWithTenantID: make(map[string]bool),
tablesWithDeletedAt: make(map[string]bool),
}
for _, opt := range opts {
opt(validator)
}
// If no SQL rewriting is enabled, return original SQL
if !validator.enableTenantInjection && !validator.enableSoftDeleteInjection && !validator.enableHiddenKBFilter &&
!validator.enableChunkEnabledFilter && !validator.enableSearchScopeFilter {
return sql, validationResult, nil
}
// Parse again to get normalized SQL
result, err := pg_query.Parse(sql)
if err != nil {
return "", validationResult, fmt.Errorf("failed to parse SQL: %v", err)
}
// Normalize SQL
normalizedSQL, err := pg_query.Deparse(result)
if err != nil {
return "", validationResult, fmt.Errorf("failed to normalize SQL: %v", err)
}
// Build table→alias map from parse tree (respects SQL aliases like "kb", "k")
tablesInQuery := extractTableAliasMap(result)
// Inject tenant conditions
securedSQL := validator.injectTenantConditions(normalizedSQL, tablesInQuery)
// Inject deleted_at IS NULL conditions
securedSQL = validator.injectSoftDeleteConditions(securedSQL, tablesInQuery)
// Inject hidden KB filter (exclude is_temporary = true knowledge bases)
securedSQL = validator.injectHiddenKBFilter(securedSQL, tablesInQuery)
// Exclude disabled chunks from model-visible query results.View on GitHub (pinned to 988cbb0330)