phacility/phabricator · error · Exception

Build step " " has step group key " ", but no step group…

Error message

Build step "%s" has step group key "%s", but no step group with that key exists.

What it means

The 'Add Step' screen enumerates every registered build step implementation (HarbormasterBuildStepImplementation::getImplementations()) and groups them by getBuildStepGroupKey(); the keys must correspond to a HarbormasterBuildStepGroup subclass discovered by PhutilClassMapQuery (each group exposes a GROUPKEY constant). This exception means some implementation returns a group key for which no group class exists, so the Add Step page cannot be rendered at all.

Solutions

  1. Make the step return an existing group key (e.g. the key of the default/normal group) unless it needs its own section.
  2. If a new section is intended, define a HarbormasterBuildStepGroup subclass with the matching GROUPKEY constant, load it via the library class map (phutil library map regeneration), and clear the class cache.
  3. Fix typos: the string in getBuildStepGroupKey() must byte-for-byte equal some group's GROUPKEY.
  4. After fixing, reload the Add Step page; the exception is per-request, no data is affected.

Example fix

// before (custom step)
public function getBuildStepGroupKey() {
  return 'mycustom';
}

// after (custom step reuses an existing group)
public function getBuildStepGroupKey() {
  return HarbormasterNormalBuildStepGroup::GROUPKEY;
}

// after (or register the group it wants)
final class MyCustomBuildStepGroup extends HarbormasterBuildStepGroup {
  const GROUPKEY = 'mycustom';
  public function getGroupName() { return pht('Custom Steps'); }
  public function getGroupOrder() { return 9000; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Extension boot check: fail at startup, not on the Add Step page
$all_groups = HarbormasterBuildStepGroup::getAllGroups();
foreach (HarbormasterBuildStepImplementation::getImplementations() as $impl) {
  if (empty($all_groups[$impl->getBuildStepGroupKey()])) {
    throw new PhutilProxyException('bad step group', ...); // during extension init
  }
}

Type guard

function stepGroupKeyIsRegistered($key) {
  return array_key_exists($key, HarbormasterBuildStepGroup::getAllGroups());
}
// usage in the step:
public function getBuildStepGroupKey() {
  $key = 'mycustom';
  if (!stepGroupKeyIsRegistered($key)) {
    return HarbormasterNormalBuildStepGroup::GROUPKEY;
  }
  return $key;
}

Prevention

When it happens

Trigger: A custom step implementation whose getBuildStepGroupKey() returns a misspelled or invented key (e.g. 'custom/group' instead of an existing GROUPKEY); a custom group class that is not loaded by the library class map (so the key never registers); an upgrade that renamed a group key while an out-of-tree step still returns the old one.

Common situations: Deploying a custom Phabricator extension that adds a step but forgets the matching group class, or forgets to add the class to the library map; running an unrebased fork after upstream renamed group keys.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/4b6b998d9a2e6e08. Report an issue: GitHub.

Appendix: source

Thrown at src/applications/harbormaster/controller/HarbormasterStepAddController.php:33

          PhabricatorPolicyCapability::CAN_EDIT,
        ))
      ->executeOne();
    if (!$plan) {
      return new Aphront404Response();
    }

    $plan_id = $plan->getID();
    $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
    $plan_title = pht('Plan %d', $plan_id);

    $all = HarbormasterBuildStepImplementation::getImplementations();
    $all = msort($all, 'getName');

    $all_groups = HarbormasterBuildStepGroup::getAllGroups();
    foreach ($all as $impl) {
      $group_key = $impl->getBuildStepGroupKey();
      if (empty($all_groups[$group_key])) {
        throw new Exception(
          pht(
            'Build step "%s" has step group key "%s", but no step group '.
            'with that key exists.',
            get_class($impl),
            $group_key));
      }
    }

    $groups = mgroup($all, 'getBuildStepGroupKey');
    $boxes = array();

    $enabled_groups = HarbormasterBuildStepGroup::getAllEnabledGroups();
    foreach ($enabled_groups as $group) {
      $list = id(new PHUIObjectItemListView())
        ->setNoDataString(
          pht('This group has no available build steps.'));

      $steps = idx($groups, $group->getGroupKey(), array());

View on GitHub (pinned to 5720a38cfe)