{"record":{"id":"24ed8a06d4fe8e05","repo":"jax-ml/jax","slug":"custom-call-backend-config-unexpected-type-backe","errorCode":null,"errorMessage":"custom_call backend_config unexpected type: {backend_config}","messagePattern":"custom_call backend_config unexpected type: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/_src/interpreters/mlir.py","lineNumber":3404,"sourceCode":"  operands = list(operands)\n\n  if backend_config is None:\n    backend_config_attr = ir.StringAttr.get(\"\")\n  elif isinstance(backend_config, (str, bytes)):\n    backend_config_attr = ir.StringAttr.get(backend_config)\n  elif isinstance(backend_config, dict):\n    # TODO(necula): it seems that the CustomCallOp constructor requires that\n    # backend_config_attr be a string attribute, even though in some cases we\n    # need it to be a DictAttr, e.g., for ApproxTopK on TPU.\n    # \"Verification failed: 'stablehlo.custom_call' op attribute 'backend_config' failed to satisfy constraint: string attribute\"\n    # To workaround this limitation we first set it to the empty string and we\n    # use an unregistered attribute mhlo.backend_config to hold the DictAttr.\n    # We must also use api_version=1 to ensure that mhlo.backend_config is\n    # handled properly.\n    backend_config_attr = ir.StringAttr.get(\"\")\n    api_version = 1\n  else:\n    raise ValueError(\"custom_call backend_config unexpected type: \" + str(backend_config))\n  attributes = dict(\n      call_target_name=ir.StringAttr.get(call_target_name),\n      has_side_effect=ir.BoolAttr.get(has_side_effect),\n      backend_config=backend_config_attr,\n      api_version=i32_attr(api_version),\n      called_computations=ir.ArrayAttr.get(\n          [ir.FlatSymbolRefAttr.get(name) for name in called_computations]\n      ),\n  )\n  if operand_output_aliases is not None:\n    attributes[\"output_operand_aliases\"] = ir.ArrayAttr.get([\n      hlo.OutputOperandAlias.get(\n          # if len(result_types) == 1 then the aliasing refers implicitly to\n          # the only output.\n          output_tuple_indices=[output_idx] if len(result_types) > 1 else [],\n          operand_index=input_idx,\n          operand_tuple_indices=[],\n      )","sourceCodeStart":3386,"sourceCodeEnd":3422,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/interpreters/mlir.py#L3386-L3422","documentation":"When emitting an MLIR stablehlo.custom_call, the backend_config parameter must be one of a few expected types (typically a string/bytes, None, or a DictAttr depending on api_version). This raise is the catch-all for any other Python type passed as backend_config.","triggerScenarios":"Registering a custom-call target via ir_generation helpers where backend_config is passed as e.g. an int, list, or arbitrary object instead of a str/bytes/None/dict-attr. Common when hand-writing xla_client custom call rules or extending mlir.py's call_op builders.","commonSituations":"Third-party JAX extensions building custom calls with non-string backend configs; version upgrades that changed the accepted backend_config types (e.g. the migration to api_version=2 string configs).","solutions":["Pass backend_config as a str (serialized, e.g. JSON or protobuf text) or None","If passing dict-like config, serialize it to a string yourself rather than passing a Python dict","Check the api_version expectations: version 2 expects a string/bytes backend_config"],"exampleFix":"# before\nbackend_config = {'grid': (8, 8)}\n\n# after\nimport json\nbackend_config = json.dumps({'grid': (8, 8)})","handlingStrategy":"type-guard","validationCode":"assert backend_config is None or isinstance(backend_config, (str, bytes)), \\\n    f'backend_config must be str/bytes/None, got {type(backend_config)}'","typeGuard":"def is_valid_backend_config(bc) -> bool:\n    return bc is None or isinstance(bc, (str, bytes))","tryCatchPattern":null,"preventionTips":["Always serialize custom-call configs to JSON/protobuf strings","Wrap custom_call emission helpers with typed assertions in extension code"],"tags":["jax","mlir","custom-call","backend-config","type-error"],"backgroundTag":"invalid-argument-type","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}