{"record":{"id":"0e0b47b30e1f59d9","repo":"koala73/worldmonitor","slug":"read-back-after-put-did-not-confirm-expected-filters-n","errorCode":null,"errorMessage":"Read-back after PUT did not confirm expected filters.\\n${missingAfter.length ? `  missing generated: ${missingAfter.slice(0, 5).join(' | ')}\\n` : ''}${missingProtected.length ? `  missing protected: ${missingProtected.join(' | ')}\\n` : ''}","messagePattern":"Read-back after PUT did not confirm expected filters\\.\\\\n(.+?)\\\\n` : ''\\}(.+?)\\\\n` : ''\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/sync-sentry-convex-probe-filters.mjs","lineNumber":453,"sourceCode":"  }\n\n  await putErrorMessageFilters(token, org, project, mergedText);\n\n  const after = await fetchProjectOptions(token, org, project);\n  const afterOptions = /** @type {Record<string, unknown>} */ (after.options ?? {});\n  const readBack =\n    typeof afterOptions['filters:error_messages'] === 'string'\n      ? afterOptions['filters:error_messages']\n      : '';\n  const readBackLines = splitErrorMessageFilters(readBack);\n  const readBackSet = new Set(readBackLines);\n\n  const missingAfter = patterns.filter((line) => !readBackSet.has(line));\n  const missingProtected = PROTECTED_ERROR_MESSAGE_FILTERS.filter(\n    (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    );","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/scripts/sync-sentry-convex-probe-filters.mjs#L435-L471","documentation":"After PUT, runApply reads back the project options and verifies that all expected generated patterns and all protected filters are present. If any are missing it throws with up to 5 missing generated lines and the full list of missing protected lines, indicating Sentry did not persist the filters exactly as sent.","triggerScenarios":"The read-back filters:error_messages set lacks one or more patterns just PUT: Sentry truncated or normalized the value (length limits, newline handling), a concurrent writer overwrote options, or the PUT actually failed silently (proxy/edge behavior).","commonSituations":"Filter text exceeding Sentry's option size limit causing truncation, another team member or automation saving project options concurrently, or a reverse proxy accepting the PUT but dropping the body.","solutions":["Re-run the sync; transient overwrites often resolve on a second attempt.","Check the read-back value size against Sentry's option length limits; shorten patterns if truncated.","Check for concurrent writers to the project's filters:error_messages option and coordinate a maintenance window.","Inspect the read-back value in Sentry UI to see exactly what persisted."],"exampleFix":"// before\n// 500 patterns joined in one PUT value -> truncated by Sentry\n// after\n// batch the PUT or reduce generated patterns to essentials, then re-verify read-back","handlingStrategy":"retry","validationCode":"const payload = patterns.join('\\n');\nif (payload.length > MAX_OPTION_LENGTH) throw new Error('Filter payload exceeds Sentry option size limit');","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  await putErrorMessageFilters(patterns, ...);\n  const readBack = await fetchProjectOptions(...);\n  if (patterns.every(p => readBack.has(p))) break;\n  await sleep(1000 * (attempt + 1));\n}","preventionTips":["Keep total filter payload within Sentry option size limits to avoid truncation.","Coordinate with anyone else who edits project options.","Always read back after PUT and treat the read-back as the source of truth."],"tags":["sentry","api","verification","write-failed"],"backgroundTag":"unexpected-response-shape","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"}