{"record":{"id":"9add33de8633343c","repo":"gofr-dev/gofr","slug":"array-index-out-of-bounds","errorCode":null,"errorMessage":"array index out of bounds","messagePattern":"array index out of bounds","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/rbac/middleware.go","lineNumber":71,"sourceCode":"\terrEmptyClaimPath = errors.New(\"empty claim path\")\n\n\t// errClaimPathNotFound is returned when a claim path is not found in JWT claims.\n\terrClaimPathNotFound = errors.New(\"claim path not found\")\n\n\t// errInvalidArrayNotation is returned when array notation is invalid.\n\terrInvalidArrayNotation = errors.New(\"invalid array notation\")\n\n\t// errInvalidArrayIndex is returned when array index is invalid.\n\terrInvalidArrayIndex = errors.New(\"invalid array index\")\n\n\t// errClaimKeyNotFound is returned when a claim key is not found.\n\terrClaimKeyNotFound = errors.New(\"claim key not found\")\n\n\t// errClaimValueNotArray is returned when a claim value is not an array.\n\terrClaimValueNotArray = errors.New(\"claim value is not an array\")\n\n\t// errArrayIndexOutOfBounds is returned when array index is out of bounds.\n\terrArrayIndexOutOfBounds = errors.New(\"array index out of bounds\")\n\n\t// errInvalidClaimStructure is returned when claim structure is invalid.\n\terrInvalidClaimStructure = errors.New(\"invalid claim structure\")\n\n\t// errAuthorizationError is returned as a generic error message for unknown errors in traces.\n\terrAuthorizationError = errors.New(\"authorization error\")\n)\n\n// Middleware creates an HTTP middleware function that enforces RBAC authorization.\n// It extracts the user's role and checks if the role is allowed for the requested route.\n//\n//nolint:gocognit,gocyclo // Middleware complexity is acceptable due to multiple authorization paths\nfunc Middleware(config *Config) func(handler http.Handler) http.Handler {\n\treturn func(handler http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t// If config is nil, allow all requests (fail open)\n\t\t\tif config == nil {\n\t\t\t\thandler.ServeHTTP(w, r)","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/rbac/middleware.go#L53-L89","documentation":"errArrayIndexOutOfBounds is returned by extractArrayClaim when the index in the claim path is a valid integer but exceeds the bounds of the actual array in the JWT claims (index < 0 or index >= len(arr)). The error message includes the requested index and the array length, e.g. \"array index out of bounds: 2 (length: 1)\". It means the configured path assumes more elements than the token carries.","triggerScenarios":"JWTClaimPath \"roles[1]\" or \"roles[2]\" while the token's \"roles\" array contains fewer elements (e.g. a user with a single role); any negative index like \"roles[-1]\"; users whose role list is empty hit this even when other users work fine.","commonSituations":"Hardcoding an index that is valid for admins but not for regular users; a Keycloak/IdP change that reduces the roles a client receives; per-tenant token differences where some tenants assign zero roles; load-balancing across realms with different role mappings.","solutions":["Point JWTClaimPath at the first element (\"roles[0]\") if any single role suffices, or at a claim guaranteed present for all users.","Ensure the identity provider always includes at least one role (e.g. a default role) in every issued token.","Prefer a non-array claim (a flat role string) when your authorization model allows one role per user.","If role sets vary per user, use a custom ErrorHandler to return a clear 401/403 message when the expected index is missing."],"exampleFix":"// before (only one role is ever assigned)\nconfig.JWTClaimPath = \"roles[1]\"\n// after\nconfig.JWTClaimPath = \"roles[0]\"","handlingStrategy":"validation","validationCode":"func indexInBounds(claims jwt.MapClaims, key string, idx int) bool {\n    arr, ok := claims[key].([]any)\n    return ok && idx >= 0 && idx < len(arr)\n}\n// sample-token check at startup:\n// if !indexInBounds(sampleClaims, \"roles\", 0) { log.Fatal(\"roles array empty or missing\") }","typeGuard":"func safeRole(claims jwt.MapClaims, key string, idx int) (string, bool) {\n    arr, ok := claims[key].([]any)\n    if !ok || idx < 0 || idx >= len(arr) { return \"\", false }\n    s, ok := arr[idx].(string)\n    return s, ok\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"array index out of bounds\") {\n    logger.Warn(\"user token has fewer roles than the configured index\", \"err\", err)\n    http.Error(w, \"unauthorized\", http.StatusUnauthorized)\n    return\n}","preventionTips":["Prefer index 0 or a non-array claim over hardcoded higher indexes","Require the IdP to assign at least one (default) role to every user","Test RBAC with tokens from the least-privileged users, not just admins"],"tags":["go","jwt","rbac","claims"],"backgroundTag":"jwt-claim-index-out-of-bounds","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}