{"record":{"id":"fd973ca6fe5bc5d1","repo":"hankcs/HanLP","slug":"join-tag-sequence","errorCode":null,"errorMessage":"\" \".join(tag_sequence)","messagePattern":"\" \"\\.join\\(tag_sequence\\)","errorType":"exception","errorClass":"InvalidTagSequence","httpStatus":null,"severity":"error","filePath":"hanlp/utils/span_util.py","lineNumber":348,"sourceCode":"        A list of string class labels `excluding` the bio tag\n        which should be ignored when extracting spans.\n\n    # Returns\n\n    spans : `List[TypedStringSpan]`\n        The typed, extracted spans from the sequence, in the format (label, (span_start, span_end)).\n        Note that the label `does not` contain any BIO tag prefixes.\n    \"\"\"\n    classes_to_ignore = classes_to_ignore or []\n    spans: Set[Tuple[str, Tuple[int, int]]] = set()\n    span_start = 0\n    span_end = 0\n    active_conll_tag = None\n    for index, string_tag in enumerate(tag_sequence):\n        # Actual BIO tag.\n        bio_tag = string_tag[0]\n        if bio_tag not in [\"B\", \"I\", \"O\"]:\n            raise InvalidTagSequence(tag_sequence)\n        conll_tag = string_tag[2:]\n        if bio_tag == \"O\" or conll_tag in classes_to_ignore:\n            # The span has ended.\n            if active_conll_tag is not None:\n                spans.add((active_conll_tag, (span_start, span_end)))\n            active_conll_tag = None\n            # We don't care about tags we are\n            # told to ignore, so we do nothing.\n            continue\n        elif bio_tag == \"B\":\n            # We are entering a new span; reset indices\n            # and active tag to new span.\n            if active_conll_tag is not None:\n                spans.add((active_conll_tag, (span_start, span_end)))\n            active_conll_tag = conll_tag\n            span_start = index\n            span_end = index\n        elif bio_tag == \"I\" and conll_tag == active_conll_tag:","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/hankcs/HanLP/blob/ddb1299bddff079e447af52ec12549c50636bfa8/hanlp/utils/span_util.py#L330-L366","documentation":"bio_tags_to_spans requires every tag in the sequence to start with B, I or O. A tag like 'E-X', 'S-X', '' (empty), or a raw class name without a prefix raises InvalidTagSequence with the whole sequence.","triggerScenarios":"Calling bio_tags_to_spans (via predict_data or evaluate_iob2) on tags in IOBES/BMES encoding (which contain S-/E-), or on malformed tags missing the BIO prefix.","commonSituations":"Mixing pipelines: feeding IOBES predictions from a neural model into an evaluator that expects IOB2; tags read from file without prefixes; downstream code stripping the B-/I- markers during post-processing.","solutions":["Convert IOBES to BIO first: map S-X→B-X, E-X→I-X (and keep B/I), or use an iobes-normalizing utility, before calling bio_tags_to_spans.","If your model outputs BMES/IOBES, use the matching evaluator (e.g. evaluate_iobes) instead of the BIO-based one.","Sanity-check the first character of each predicted tag in your decoding step."],"exampleFix":"# before\nspans = bio_tags_to_spans(pred_tags)  # tags like ['S-PER','E-PER']\n# after\nfixed = [('B' + t[1:] if t[0] == 'S' else 'I' + t[1:] if t[0] == 'E' else t) for t in pred_tags]\nspans = bio_tags_to_spans(fixed)","handlingStrategy":"validation","validationCode":"assert all(t and t[0] in 'BIO' for t in tags), 'tags must be BIO-encoded'","typeGuard":"def is_bio_sequence(tags):\n    return all(isinstance(t, str) and len(t) >= 2 and t[0] in 'BIO' and t[1] == '-' for t in tags)","tryCatchPattern":null,"preventionTips":["Convert IOBES/BMES predictions to BIO before span extraction.","Pick the evaluator matching your tag scheme; add a scheme check to your eval harness."],"tags":["nlp","ner","tag-scheme","validation"],"backgroundTag":"invalid-tag-sequence","analyzedSha":"ddb1299bddff079e447af52ec12549c50636bfa8","analyzedAt":"2026-08-27T03:36:54.287Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}