{"record":{"id":"9071454b8baf8236","repo":"phacility/phabricator","slug":"field-label-s-is-parsed-by-two-custom-fields","errorCode":null,"errorMessage":"Field label \"%s\" is parsed by two custom fields: \"%s\" and \"%s\". Each label must be parsed by only one field.","messagePattern":"Field label \"(.+?)\" is parsed by two custom fields: \"(.+?)\" and \"(.+?)\"\\. Each label must be parsed by only one field\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/differential/parser/DifferentialCommitMessageParser.php","lineNumber":369,"sourceCode":"  }\n\n\n/* -(  Internals  )---------------------------------------------------------- */\n\n\n  private function getLabelMap() {\n    if ($this->labelMap === null) {\n      $field_list = $this->getCommitMessageFields();\n\n      $label_map = array();\n      foreach ($field_list as $field_key => $field) {\n        $labels = $field->getFieldAliases();\n        $labels[] = $field->getFieldName();\n\n        foreach ($labels as $label) {\n          $normal_label = self::normalizeFieldLabel($label);\n          if (!empty($label_map[$normal_label])) {\n            throw new Exception(\n              pht(\n                'Field label \"%s\" is parsed by two custom fields: \"%s\" and '.\n                '\"%s\". Each label must be parsed by only one field.',\n                $label,\n                $field_key,\n                $label_map[$normal_label]));\n          }\n\n          $label_map[$normal_label] = $field_key;\n        }\n      }\n\n      $this->labelMap = $label_map;\n    }\n\n    return $this->labelMap;\n  }\n","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/differential/parser/DifferentialCommitMessageParser.php#L351-L387","documentation":"DifferentialCommitMessageParser::getLabelMap() builds a normalized label-to-field-key map from every enabled commit-message field, using each field's getFieldAliases() plus getFieldName(). If two enabled fields claim the same normalized label, commit-message parsing would be ambiguous, so the parser throws with the label and both conflicting field keys. This is a configuration/extension conflict that surfaces the first time the parser is used after the conflicting field is enabled.","triggerScenarios":"Enabling a custom field whose field name or an alias normalizes to the same label as a core field (e.g., two fields claiming 'Reviewers' or 'Reviewed By'); a custom field subclass that does not override getFieldAliases() and inherits a parent's labels; label normalization collapsing case/punctuation differences ('Reviewer' vs 'reviewer:'); upgrading an extension that adds new aliases","commonSituations":"Adding third-party custom fields through PhabricatorCustomFieldConfigOption; local extensions cloned from core field classes; enabling two similar extensions after a Phabricator upgrade; cached field lists making the conflict appear only after cache clearing.","solutions":["Read the message: it names the label and both field keys; rename one field's getFieldName() or prune its getFieldAliases() so no label overlaps","Disable one of the two conflicting fields in the custom-field configuration if both are not needed","If your custom field intentionally mirrors a core label, override getFieldAliases() to return array() and give it a unique field name","After fixing, clear caches (phabricator/ $ ./bin/cache purge) because the enabled field list is cached"],"exampleFix":"// before (custom field duplicating a core label)\nclass MyProjectCustomField extends DifferentialCommitMessageCustomField {\n  public function getFieldName() { return pht('Reviewers'); }\n  public function getFieldAliases() { return array('Reviewed By'); }\n}\n\n// after\npublic function getFieldName() { return pht('Project Reviewers'); }\npublic function getFieldAliases() { return array(); }","handlingStrategy":"validation","validationCode":"// After changing custom-field config, smoke-test parsing before use:\nphabricator/ $ ./bin/cache purge\nphabricator/ $ echo 'Reviewed By: alice' | ./bin/differential ... # any path that parses a commit message\n// Duplicate-label conflicts throw immediately, so this surfaces the config error safely.","typeGuard":"// Config-time check: no two enabled fields share a normalized label\nfunction assertNoLabelConflicts(array $fields) {\n  $seen = array();\n  foreach ($fields as $key => $field) {\n    $labels = array_merge($field->getFieldAliases(), array($field->getFieldName()));\n    foreach ($labels as $label) {\n      $n = strtolower(preg_replace('/[^a-z0-9]/i', '', $label));\n      if (isset($seen[$n])) {\n        throw new Exception(\"Label conflict: {$key} vs {$seen[$n]} on '{$label}'\");\n      }\n      $seen[$n] = $key;\n    }\n  }\n}","tryCatchPattern":null,"preventionTips":["Give every custom commit-message field a unique label and empty getFieldAliases() unless aliases are truly needed","After enabling or upgrading custom fields, purge caches and run one commit-message parse as a smoke test","Review the exception text: it names both conflicting field keys, so the fix is a rename or a disable of one of them"],"tags":["phabricator","differential","commit-message","custom-field","config-conflict","parser"],"backgroundTag":"duplicate-config-key","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}