{"record":{"id":"867bae2bd7be8718","repo":"koala73/worldmonitor","slug":"read-back-still-contains-legacy-tutorial-probe-filters-n","errorCode":null,"errorMessage":"Read-back still contains legacy tutorial probe filters:\\n  ${leftoverLegacy.join('\\n  ')}","messagePattern":"Read-back still contains legacy tutorial probe filters:\\\\n  (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/sync-sentry-convex-probe-filters.mjs","lineNumber":468,"sourceCode":"    (line) => !readBackSet.has(line),\n  );\n  if (missingAfter.length > 0 || missingProtected.length > 0) {\n    throw new Error(\n      'Read-back after PUT did not confirm expected filters.\\n'\n        + (missingAfter.length\n          ? `  missing generated: ${missingAfter.slice(0, 5).join(' | ')}\\n`\n          : '')\n        + (missingProtected.length\n          ? `  missing protected: ${missingProtected.join(' | ')}\\n`\n          : ''),\n    );\n  }\n\n  const leftoverLegacy = LEGACY_TUTORIAL_PROBE_FILTERS.filter((line) =>\n    readBackSet.has(line),\n  );\n  if (leftoverLegacy.length > 0) {\n    throw new Error(\n      'Read-back still contains legacy tutorial probe filters:\\n  '\n        + leftoverLegacy.join('\\n  '),\n    );\n  }\n\n  console.log(\n    `apply: PASS — read-back has ${patterns.length} internal pattern(s) and `\n      + 'both protected non-probe lines; legacy tutorial patterns removed.',\n  );\n}\n\n/**\n * Return `absPath` relative to the repo root when possible.\n *\n * @param {string} absPath\n * @returns {string}\n */\nfunction relativeToRepo(absPath) {","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/scripts/sync-sentry-convex-probe-filters.mjs#L450-L486","documentation":"After PUT, runApply checks that none of LEGACY_TUTORIAL_PROBE_FILTERS remain in the read-back filters. If legacy tutorial probe filters are still present, the script throws — the migration to the new Convex probe filter set was incomplete and legacy noise filters would keep hiding events.","triggerScenarios":"The read-back filters:error_messages still contains one or more known legacy tutorial probe filter lines that the merge was supposed to remove, meaning the removal from the PUT payload didn't take effect on the server.","commonSituations":"Sentry merging rather than replacing option values, stale legacy filters re-added by another automation, or a PUT payload that preserved legacy lines because the script version predates the legacy list.","solutions":["Update the script to the latest version so LEGACY_TUTORIAL_PROBE_FILTERS removal is included in the PUT payload.","Manually remove the legacy lines from filters:error_messages in the Sentry UI, then re-run the sync.","Verify the PUT uses a full replacement value (not an append) of merged filters."],"exampleFix":"// before\nconst mergedText = [...existing, ...appended].join('\\n');  // legacy lines preserved\n// after\nconst mergedText = [...existing, ...appended].filter(l => !LEGACY_TUTORIAL_PROBE_FILTERS.includes(l)).join('\\n');","handlingStrategy":"validation","validationCode":"const leftovers = LEGACY_TUTORIAL_PROBE_FILTERS.filter(l => readBackSet.has(l));\nif (leftovers.length) { console.error('Manually remove from Sentry UI:', leftovers); process.exit(1); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep LEGACY_TUTORIAL_PROBE_FILTERS updated as legacy filters are discovered.","Ensure the PUT replaces the full option value rather than appending.","After manual UI edits, re-run the sync to converge state."],"tags":["sentry","migration","config","verification"],"backgroundTag":"invalid-state-transition","analyzedSha":"e586b8b4b80f595aa7ece295eec10d76f2921240","analyzedAt":"2026-09-22T01:50:49.965Z","contentChangedAt":"2026-09-22T01:50:49.965Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}