gitlabhq/gitlabhq · error · Gitlab::Graphql::Errors::ArgumentError

Non-admin users must provide a groupId, projectId, or curren

Error message

Non-admin users must provide a groupId, projectId, or current username

What it means

The root `timelogs` GraphQL query exposes all time-tracking data, so Resolvers::TimelogResolver#validate_args! requires a scope: a parent object (the field is normally used under a project or group), a `groupId`/`projectId` argument, your own `username`, or admin capability (`can_read_all_resources?`). Without one of these, a non-admin gets ArgumentError with this message. This is a data-exposure guard, not a syntax check.

Source

Thrown at app/graphql/resolvers/timelog_resolver.rb:118

    def parse_datetime_args(args)
      if times_provided?(args)
        args
      else
        parsed_args = args.except(:start_date, :end_date)

        parsed_args[:start_time] = args[:start_date].beginning_of_day if args[:start_date]
        parsed_args[:end_time] = args[:end_date].end_of_day if args[:end_date]

        parsed_args
      end
    end

    def times_provided?(args)
      args[:start_time] && args[:end_time]
    end

    def raise_argument_error(message)
      raise Gitlab::Graphql::Errors::ArgumentError, message
    end
  end
end

View on GitHub (pinned to 55ee20384a)

Solutions

  1. Add a scope argument to the query: `groupId`, `projectId`, or your own `username`
  2. If instance-wide timelogs are genuinely needed, use an account with admin read access (custom admin role with can_read_all_resources) — otherwise scope per group/project
  3. If you intended your own timelogs, set `username` to exactly your authenticated user's username

Example fix

# before
query {
  timelogs(startDate: "2026-08-01") { nodes { timeSpent user { username } } }
}

# after
query {
  timelogs(
    startDate: "2026-08-01"
    groupId: "gid://gitlab/Group/507"
  ) { nodes { timeSpent user { username } } }
}
Defensive patterns

Strategy: validation

Validate before calling

function assertTimelogScope(args, currentUser) {
  const scoped = args.groupId || args.projectId ||
    (args.username && args.username === currentUser?.username);
  if (!scoped && !currentUser?.admin) {
    throw new Error('timelogs: non-admin must pass groupId, projectId, or own username');
  }
}

Type guard

function isScopedTimelogQuery(args, currentUser) {
  return Boolean(args.groupId ?? args.projectId ?? (args.username === currentUser?.username));
}

Try / catch

catch (err) {
  if (err.graphqlErrors?.some(e => e.message.startsWith('Non-admin users must provide'))) {
    promptUserToPickGroupOrProject(); // degrade to scoped query UI
  }
}

Prevention

When it happens

Trigger: `query { timelogs(startDate: ...) { nodes { timeSpent } } }` at the root with no groupId/projectId/username; passing another user's username while not being an admin; a script that worked under an admin token then run with a regular PAT.

Common situations: Personal access tokens swapped from admin to non-admin during a security cleanup; dashboards ported from the REST timelog endpoints that defaulted to instance-wide data; forgetting the scope argument when moving from the group-scoped field to the root query.

Related errors


AI-assisted analysis of gitlabhq/gitlabhq@55ee20384a (2026-08-21). Data as JSON: /api/errors/18e4b308fa1cd34b. Report an issue: GitHub.