{"record":{"id":"f906337e03dc84bb","repo":"phacility/phabricator","slug":"service-type-s-is-unrecognized-valid-types-are","errorCode":null,"errorMessage":"Service type \"%s\" is unrecognized. Valid types are: %s.","messagePattern":"Service type \"(.+?)\" is unrecognized\\. Valid types are: (.+?)\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/almanac/editor/AlmanacServiceEditEngine.php","lineNumber":63,"sourceCode":"    $type = null;\n    foreach ($raw_xactions as $raw_xaction) {\n      if ($raw_xaction['type'] !== 'type') {\n        continue;\n      }\n\n      $type = $raw_xaction['value'];\n    }\n\n    if ($type === null) {\n      throw new Exception(\n        pht(\n          'When creating a new Almanac service via the Conduit API, you '.\n          'must provide a \"type\" transaction to select a type.'));\n    }\n\n    $map = AlmanacServiceType::getAllServiceTypes();\n    if (!isset($map[$type])) {\n      throw new Exception(\n        pht(\n          'Service type \"%s\" is unrecognized. Valid types are: %s.',\n          $type,\n          implode(', ', array_keys($map))));\n    }\n\n    $this->setServiceType($type);\n\n    return $this->newEditableObject();\n  }\n\n  protected function newEditableObjectForDocumentation() {\n    $service_type = new AlmanacCustomServiceType();\n    $this->setServiceType($service_type->getServiceTypeConstant());\n    return $this->newEditableObject();\n  }\n\n  protected function newObjectQuery() {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/almanac/editor/AlmanacServiceEditEngine.php#L45-L81","documentation":"Thrown by AlmanacServiceEditEngine when a new Almanac service is created through the Conduit API ('almanac.service.edit') and the required 'type' transaction carries a value that is not a registered service type constant. The engine collects the last 'type' transaction from the raw transaction list, looks it up in AlmanacServiceType::getAllServiceTypes(), and rejects unknown keys. The message helpfully lists every valid constant (built-ins: almanac.custom, cluster.database, cluster.repository, drydock.pool).","triggerScenarios":"Calling almanac.service.edit with object PHID null (create) and transactions [{\"type\":\"type\",\"value\":\"custom\"}] instead of \"almanac.custom\"; passing a class name like \"AlmanacCustomServiceType\" instead of the SERVICETYPE constant; passing a cluster type such as \"cluster.cache\" that does not exist in this install; omitting quoting so the shell/API mangles the dotted constant.","commonSituations":"Scripts or automation provisioning Almanac services that hardcode a guessed type string; code written against a different Phabricator/Phorge version whose cluster service types differ; copy/paste from UI labels (\"Custom Service\") instead of the machine constant; custom extensions that register a service type but the Conduit call runs before the class is loadable.","solutions":["Read the valid list straight from the error message (or AlmanacServiceType::getAllServiceTypes()) and use one of those exact constants, e.g. \"almanac.custom\" for a generic service.","Make sure the create request actually includes a transactions entry of type \"type\" whose value is the constant string, and that no later transaction overwrites it with a bad value (the engine takes the LAST one).","If automating, first call almanac.servicetype.search (or the getAllServiceTypes map in-process) to discover the type constants supported by this install instead of hardcoding them.","When adding a custom service type in an extension, verify the class extends AlmanacServiceType, defines SERVICETYPE (max 64 chars), and is reachable via phutil_map_instances so it appears in the map."],"exampleFix":"// before (Conduit: almanac.service.edit, creating)\n$transactions = array(\n  array('type' => 'name', 'value' => 'my-service'),\n  array('type' => 'type', 'value' => 'custom'),\n);\n\n// after\n$transactions = array(\n  array('type' => 'name', 'value' => 'my-service'),\n  array('type' => 'type', 'value' => 'almanac.custom'),\n);","handlingStrategy":"validation","validationCode":"$valid = array_keys(AlmanacServiceType::getAllServiceTypes());\nif (!in_array($my_type, $valid, true)) {\n  throw new InvalidArgumentException(\n    \"Unknown service type '{$my_type}'. Valid: \".implode(', ', $valid));\n}","typeGuard":"function is_valid_almanac_service_type(string $type): bool {\n  return isset(AlmanacServiceType::getAllServiceTypes()[$type]);\n}","tryCatchPattern":"// When calling conduit 'almanac.service.edit' from PHP:\ntry {\n  $result = $client->callMethodSynchronous('almanac.service.edit', $params);\n} catch (CommandException $e) {\n  // Conduit errors surface with the pht() message; detect type errors and\n  // re-fetch the valid list from the message or almanac.servicetype data.\n  if (preg_match('/Service type .* is unrecognized/', $e->getMessage())) {\n    // fall back to 'almanac.custom' or abort with the valid list\n  }\n}","preventionTips":["Discover type constants programmatically (AlmanacServiceType::getAllServiceTypes() or a servicetype search) instead of hardcoding strings.","In create pipelines, assert the type transaction is present exactly once with a known constant before submitting.","Pin automation to the exact Phabricator/Phorge version and re-validate constants after upgrades."],"tags":["phabricator","almanac","conduit","validation","edit-engine"],"backgroundTag":"invalid-enum-value","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}