{"record":{"id":"d0faed479b097fad","repo":"gofr-dev/gofr","slug":"query-cannot-be-empty","errorCode":null,"errorMessage":"query cannot be empty","messagePattern":"query cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/datasource/elasticsearch/elasticsearch.go","lineNumber":27,"sourceCode":"\t\"strings\"\n\t\"time\"\n\n\tes \"github.com/elastic/go-elasticsearch/v8\"\n\t\"github.com/elastic/go-elasticsearch/v8/esapi\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nconst (\n\tstatusDown     = \"DOWN\"\n\tstatusUp       = \"UP\"\n\tdefaultTimeout = 5 * time.Second\n)\n\nvar (\n\terrEmptyIndex        = errors.New(\"index name cannot be empty\")\n\terrEmptyDocumentID   = errors.New(\"document ID cannot be empty\")\n\terrEmptyQuery        = errors.New(\"query cannot be empty\")\n\terrEmptyOperations   = errors.New(\"operations cannot be empty\")\n\terrHealthCheckFailed = errors.New(\"elasticsearch health check failed\")\n\terrOperation         = errors.New(\"elasticsearch operation error\")\n\terrMarshaling        = errors.New(\"error marshaling data\")\n\terrParsingResponse   = errors.New(\"error parsing response\")\n\terrResponse          = errors.New(\"invalid elasticsearch response\")\n\terrEncodingOperation = errors.New(\"error encoding operation\")\n)\n\n// Config holds the configuration for connecting to Elasticsearch.\ntype Config struct {\n\tAddresses []string\n\tUsername  string\n\tPassword  string\n}\n\n// Client represents the Elasticsearch client.\ntype Client struct {","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/datasource/elasticsearch/elasticsearch.go#L9-L45","documentation":"After parsing the index, extractArrayClaim looks up the base key (text before '['). If claims[key] is absent it wraps errClaimKeyNotFound with that key. Unlike error 402 (full path not found on simple lookup), this fires specifically for array-notation paths where the array itself is missing from the token.","triggerScenarios":"Path \"roles[0]\" but the JWT has no \"roles\" key at all; issuer renamed the claim (\"groups\"/\"authorities\") while config still says \"roles[0]\"; token from a flow that strips array claims.","commonSituations":"IdP mapper changes; multi-tenant issuers with different claim schemas; service-account tokens that omit user role arrays; stale RBAC config after an auth-server migration.","solutions":["Decode a live token (jwt.io or debug logging) and set the base key to the actual array claim name","Require the claim in token issuance (scope, audience, protocol mapper) so every access token carries it","errors.Is check for errClaimKeyNotFound and return 401 with a log naming the missing array key"],"exampleFix":"// before\nextractClaimValue(claims, \"roles[0]\") // token has \"groups\": [...]\n// after\nextractClaimValue(claims, \"groups[0]\")","handlingStrategy":"validation","validationCode":"baseKey := path[:strings.Index(path, \"[\")]\nif _, ok := claims[baseKey]; !ok {\n    return fmt.Errorf(\"token lacks array claim %q\", baseKey)\n}","typeGuard":"func hasArrayClaim(claims jwt.MapClaims, key string) bool {\n    _, ok := claims[key].([]any)\n    return ok\n}","tryCatchPattern":"v, err := extractClaimValue(claims, path)\nif errors.Is(err, errClaimKeyNotFound) {\n    http.Error(w, \"missing array claim\", http.StatusUnauthorized)\n    return\n}","preventionTips":["Verify the base key name against a decoded production token","Keep IdP mappers under change control; update claim paths when schemas change","Test with tokens from every issuing flow (user, service, refresh)"],"tags":["jwt","rbac","claims"],"backgroundTag":"jwt-claim-not-found","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}