Skip to content

API-key GraphQL query spanning array and docker roots crashes Casbin enforcement #2060

Description

@zeroedsolutions

Environment

  • Unraid OS: 7.3.2
  • Unraid API: 4.35.1+a9625ae2
  • Authentication: API key with read access to both ARRAY and DOCKER resources
  • Transport: local GraphQL endpoint

No hostnames, addresses, API-key values, or registration data are included.

Minimal reproduction

A single request containing one field from each root fails:

query MinimalArrayDocker {
  array { state }
  docker { containers { names } }
}

HTTP 200 response:

{
  "data": null,
  "errors": [
    {
      "message": "Cannot read properties of undefined (reading '0')",
      "path": ["array"],
      "code": "INTERNAL_SERVER_ERROR"
    }
  ]
}

The order of the two root fields does not change the result.

Controls

On the same host and with the same API key:

query ArrayOnly { array { state } }

and

query DockerOnly { docker { containers { names } } }

both succeed when sent as separate HTTP requests. Five consecutive rounds produced:

  • combined request: 5/5 failed at path array
  • array-only request: 5/5 passed
  • docker-only request: 5/5 passed

Broader incremental tests also showed that array alone succeeds and begins failing as soon as any docker child selection is added to the same operation.

Server stack

PM2 application logs identify the failure in Casbin:

TypeError: Cannot read properties of undefined (reading '0')
    at /usr/local/unraid-api/node_modules/casbin/lib/cjs/coreEnforcer.js:422:90
    at Array.forEach (<anonymous>)
    at Enforcer.privateEnforce (.../casbin/lib/cjs/coreEnforcer.js:421:64)

At Casbin 5.38.0, line 422 reads from p.policy[i][j] after policyLen was captured.

Source-level observation / hypothesis

Verified source facts in tag v4.35.1:

  • AuthService.validateApiKeyCasbin() synchronizes API-key roles and permissions during validation.
  • syncApiKeyPermissions() deletes the key's existing permissions and re-adds permission/action pairs through batchProcess().
  • batchProcess() starts all add operations together and waits with Promise.allSettled().

Hypothesis, not yet proved: concurrent policy additions or concurrent permission enforcement leaves/observes an undefined policy row. This would explain why either root succeeds alone while two permission-protected roots in one GraphQL operation reach p.policy[i] === undefined.

Expected behavior

A read-only GraphQL operation spanning two authorized roots should return both roots without an internal authorization-engine error. At minimum, authorization failure should produce a bounded authorization error rather than INTERNAL_SERVER_ERROR and null the complete response.

Known-issue check

As of 2026-08-10, searches of open and closed unraid/api issues for the exact exception, array resolver, Casbin, and multi-root API-key queries did not find a matching report. Release notes and commits after 4.35.1 did not identify this specific failure. The deployed host was not upgraded to test a later API version.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions