{"record":{"id":"9f9a55542b3692a6","repo":"apache/beam","slug":"replacement-string-since-not-found-on-custom-message","errorCode":null,"errorMessage":"Replacement string %since% not found on custom message","messagePattern":"Replacement string (.+?)ince% not found on custom message","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/utils/annotations.py","lineNumber":104,"sourceCode":"\n# Don't ignore BeamDeprecationWarnings.\nwarnings.simplefilter('once', BeamDeprecationWarning)\n\n\nclass _WarningMessage:\n  \"\"\"Utility class for assembling the warning message.\"\"\"\n  def __init__(self, label, since, current, extra_message, custom_message):\n    \"\"\"Initialize message, leave only name as placeholder.\"\"\"\n    if custom_message is None:\n      message = '%name% is ' + label\n      if label == 'deprecated':\n        message += ' since %s' % since\n      message += '. Use %s instead.' % current if current else '.'\n      if extra_message:\n        message += ' ' + extra_message\n    else:\n      if label == 'deprecated' and '%since%' not in custom_message:\n        raise TypeError(\n            \"Replacement string %since% not found on \\\n        custom message\")\n      emptyArg = lambda x: '' if x is None else x\n      message = custom_message\\\n      .replace('%since%', emptyArg(since))\\\n      .replace('%current%', emptyArg(current))\\\n      .replace('%extra%', emptyArg(extra_message))\n    self.label = label\n    self.message = message\n\n  def emit_warning(self, fnc_name):\n    if self.label == 'deprecated':\n      warning_type = BeamDeprecationWarning\n    else:\n      warning_type = FutureWarning\n    warnings.warn(\n        self.message.replace('%name%', fnc_name), warning_type, stacklevel=3)\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/utils/annotations.py#L86-L122","documentation":"When a deprecated/experimental function is declared with a custom_message but the label is 'deprecated', the message must contain the %since% placeholder so the version string can be substituted. Beam raises TypeError if it is missing, because the deprecation notice would lose its version information.","triggerScenarios":"Calling @deprecated(since='X', current='y', custom_message='Use y.') without '%since%' in custom_message, then applying the decorator.","commonSituations":"Copy-pasting a custom message from an @experimental decorator into an @deprecated decorator; hand-editing a deprecation message and dropping the placeholder; upgrading Beam where stricter checks apply.","solutions":["Add %since% to the custom_message string.","Switch the label to 'experimental' if no version info is intended.","Remove custom_message and let Beam build the default message from since/current.","Include both placeholders: '%since%' and optionally '%current%'."],"exampleFix":"// before\n@deprecated(since='2.0.0', current='new_fn', custom_message='Use new_fn.')\n// after\n@deprecated(since='2.0.0', current='new_fn', custom_message='Deprecated since %since%. Use %current%.')","handlingStrategy":"validation","validationCode":"if label == 'deprecated' and '%since%' not in custom_message:\n    raise ValueError('custom_message must contain %since%')","typeGuard":null,"tryCatchPattern":"try:\n    wrap = deprecated(since=since, current=cur, custom_message=msg)\nexcept TypeError as e:\n    log.warning('fixing custom_message: %s', e)\n    wrap = deprecated(since=since, current=cur, custom_message=msg + ' Deprecated since %since%.')","preventionTips":["Always include '%since%' (and '%current%') in deprecation custom messages","Reuse a message template constant instead of hand-writing messages","Don't reuse @experimental messages for @deprecated decorators"],"tags":["python","apache-beam","deprecation"],"backgroundTag":"deprecated-api-usage","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}