{"record":{"id":"a13b027de7a86e1b","repo":"OtterMind/Chat2DB","slug":"prepared-qq-notification-repository-does-not-match","errorCode":null,"errorMessage":"Prepared QQ notification repository does not match","messagePattern":"Prepared QQ notification repository does not match","errorType":"validation","errorClass":"ConfigurationError","httpStatus":null,"severity":"error","filePath":"script/github/notify_qq.py","lineNumber":255,"sourceCode":"                \"message\": message,\n            },\n            ensure_ascii=False,\n        ),\n        encoding=\"utf-8\",\n    )\n\n\ndef _read_prepared_message(path: Path, repository: str) -> tuple[str, str]:\n    if not path.is_file() or path.stat().st_size > 4096:\n        raise ConfigurationError(\"Prepared QQ notification is missing or too large\")\n    try:\n        envelope = json.loads(path.read_text(encoding=\"utf-8\"))\n    except (OSError, UnicodeError, json.JSONDecodeError) as error:\n        raise ConfigurationError(\"Prepared QQ notification is not valid JSON\") from error\n    if not isinstance(envelope, Mapping) or envelope.get(\"version\") != 1:\n        raise ConfigurationError(\"Prepared QQ notification has an invalid format\")\n    if envelope.get(\"repository\") != repository:\n        raise ConfigurationError(\"Prepared QQ notification repository does not match\")\n    event_name = str(envelope.get(\"event_name\") or \"\")\n    if event_name not in COLLECTED_EVENT_NAMES:\n        raise ConfigurationError(\"Prepared QQ notification event is not allowed\")\n    message = envelope.get(\"message\")\n    if not isinstance(message, str) or not message or len(message) > 900:\n        raise ConfigurationError(\"Prepared QQ notification message is invalid\")\n    if re.search(r\"[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]\", message):\n        raise ConfigurationError(\"Prepared QQ notification contains control characters\")\n    if re.search(r\"(?i)\\[CQ:\", message):\n        raise ConfigurationError(\"Prepared QQ notification contains a OneBot CQ code\")\n    return event_name, message\n\n\ndef _event_detail(event_name: str, action: str, payload: Mapping[str, Any]) -> str:\n    if action in {\"labeled\", \"unlabeled\"}:\n        label = payload.get(\"label\") or {}\n        return f\"标签：{_clean_text(label.get('name'), 80)}\"\n    if action in {\"assigned\", \"unassigned\"}:","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/notify_qq.py#L237-L273","documentation":"Raised by _read_prepared_message (notify_qq.py:255) when reading a previously collected notification envelope. The envelope's \"repository\" field does not equal the repository passed in, which comes from GITHUB_REPOSITORY (default \"OtterMind/Chat2DB\"). This two-step collect/send flow passes an envelope between jobs, and this guard ensures an envelope is never replayed against a different repository.","triggerScenarios":"Running the notifier with QQ_PREPARED_MESSAGE_PATH pointing at a JSON envelope whose \"repository\" value differs from the current GITHUB_REPOSITORY. Happens when a collect job runs in a fork, the file is reused across repos, or GITHUB_REPOSITORY is overridden in the dispatch.","commonSituations":"Forking the repo and replaying an envelope written by upstream; manually editing the prepared message JSON; copying an envelope file between CI matrices that set different repository identifiers; dispatching the send job with the wrong QQ_PREPARED_MESSAGE_PATH artifact.","solutions":["Regenerate the prepared message in the same repository/job that will send it (re-run the collect step with QQ_MESSAGE_OUTPUT_PATH).","Verify QQ_PREPARED_MESSAGE_PATH resolves to the artifact produced by this run's collect step, not a stale or cross-repo file.","If dispatching manually, ensure GITHUB_REPOSITORY matches the repository field embedded when the envelope was written.","Inspect the envelope JSON and confirm its \"repository\" field equals owner/name exactly (case-sensitive)."],"exampleFix":"// before: envelope {\"repository\": \"fork/Chat2DB\"} run under GITHUB_REPOSITORY=OtterMind/Chat2DB\n// after: regenerate the envelope in the sending repo, or match the value\n{\"version\": 1, \"event_name\": \"issue_comment\", \"repository\": \"OtterMind/Chat2DB\", \"message\": \"...\"}","handlingStrategy":"validation","validationCode":"# Before reading, confirm the envelope is for this repository.\nimport json\nrepo = os.environ.get(\"GITHUB_REPOSITORY\", \"OtterMind/Chat2DB\")\nenvelope = json.loads(Path(prepared_path).read_text(encoding=\"utf-8\"))\nif envelope.get(\"repository\") != repo:\n    raise SystemExit(f\"envelope repository {envelope.get('repository')!r} != {repo!r}\")","typeGuard":"def matches_repository(envelope: object, repository: str) -> bool:\n    return isinstance(envelope, Mapping) and envelope.get(\"repository\") == repository","tryCatchPattern":null,"preventionTips":["Always regenerate the prepared envelope in the same job/repository that will send it.","Pass prepared messages as job artifacts scoped to one workflow run, not shared files.","Treat a repository mismatch as a stale-artifact problem and re-run collect."],"tags":["config","github-actions","notification","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}