{"record":{"id":"fc4f8a3acd20b0a7","repo":"Tencent/WeKnora","slug":"opensearch-feature-not-enabled-in-this-build","errorCode":null,"errorMessage":"opensearch: feature not enabled in this build","messagePattern":"opensearch: feature not enabled in this build","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/repository/retriever/opensearch/errors.go","lineNumber":43,"sourceCode":"\n\t// ErrTransport — network / 5xx / opaque cluster error. Classified as\n\t// transient: ensureReady does NOT persist this in initErr, so the next\n\t// caller will retry.\n\tErrTransport = errors.New(\"opensearch: transport error\")\n\n\t// ErrVersionUnsupported — cluster is not OpenSearch, is OS 1.x, or is\n\t// OS 2.0~2.3 (pre-Lucene-HNSW-GA). probeVersion enforces.\n\tErrVersionUnsupported = errors.New(\"opensearch: cluster version unsupported\")\n\n\t// ErrConfigInvalid — IndexConfig / storeID / sanitizeIndexName guard\n\t// failed, or the k-NN plugin is missing on one or more cluster nodes.\n\tErrConfigInvalid = errors.New(\"opensearch: invalid index config\")\n\n\t// ErrFeatureNotEnabled — stubs.go returns this from methods whose real\n\t// implementation has not landed yet (CopyIndices / BatchUpdateChunk* /\n\t// swapToVersion, plus the read/write methods that a follow-up commit\n\t// will replace with production code).\n\tErrFeatureNotEnabled = errors.New(\"opensearch: feature not enabled in this build\")\n\n\t// ErrBatchTooLarge — Save / Delete batch exceeded the driver's sync\n\t// cap. Distinct from ErrFeatureNotEnabled so the service layer can\n\t// chunk + retry rather than treat the failure as \"waiting on a future\n\t// implementation.\"\n\tErrBatchTooLarge = errors.New(\"opensearch: batch size exceeds driver cap\")\n\n\t// ErrCircuitBreaker — OpenSearch k-NN circuit breaker returned 429\n\t// (knn_circuit_breaker_exception). Classified as transient so the\n\t// caller can retry after the operator scales the cluster.\n\tErrCircuitBreaker = errors.New(\"opensearch: knn circuit breaker open\")\n)\n\n// isTransientErr classifies sentinel errors. Transient errors do not get\n// persisted in ensureReady's initErr cache — they can be retried by the\n// next caller after the underlying cause clears.\nfunc isTransientErr(err error) bool {\n\treturn errors.Is(err, ErrTransport) || errors.Is(err, ErrCircuitBreaker)","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/repository/retriever/opensearch/errors.go#L25-L61","documentation":"When checkSchemaAccess is enabled, function calls qualified with more than one name part (schema.function) are only allowed from pg_catalog. A call like `myschema.my_func(...)` or `admin.exec(...)` is rejected to prevent invoking attacker-controlled or extension functions installed in non-catalog schemas.","triggerScenarios":"Validating `SELECT myschema.my_func(1)`, `SELECT public.my_helper(x)`, or `SELECT dblink_exec('...')` qualified by its extension schema, while schema access checking is on.","commonSituations":"Applications calling their own stored functions installed in an app-specific schema; extension functions that live in non-pg_catalog schemas (dblink, pgcrypto in some installs); generated code that fully qualifies every function name.","solutions":["Call unqualified function names (rely on search_path) or move/alias the function into pg_catalog if it must remain qualified.","Rewrite the logic as an in-query expression or compute it in application code instead of calling a custom-schema function.","If the schema's functions are trusted, relax the check by allowing the specific schema in the validator's schema-access logic or disabling checkSchemaAccess (trusted input only)."],"exampleFix":"// before\nSELECT myschema.my_func(user_id) FROM users;\n\n// after\nSELECT my_func(user_id) FROM users; -- search_path resolves to allowed schema","handlingStrategy":"validation","validationCode":"var qualFunc = regexp.MustCompile(`(?i)\\b([a-z_][a-z0-9_]*)\\.[a-z_][a-z0-9_]*\\s*\\(`)\nfunc usesNonCatalogQualifiedFunc(sql string) bool {\n\tfor _, m := range qualFunc.FindAllStringSubmatch(sql, -1) {\n\t\ts := strings.ToLower(m[1])\n\t\tif s != \"pg_catalog\" && s != \"public\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"if err := validator.ValidateQuery(sql); err != nil {\n\tif strings.Contains(err.Error(), \"schema-qualified function calls\") {\n\t\treturn fmt.Errorf(\"call functions unqualified or install them in pg_catalog: %w\", err)\n\t}\n}","preventionTips":["Install stored functions in a schema reachable via search_path and call them unqualified.","Avoid fully qualifying function names in generated SQL.","Keep custom/extension functions out of user-facing query paths; expose their results via views instead."],"tags":["sql","security","function-call","schema-access"],"backgroundTag":"schema-access-denied","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}