koala73/worldmonitor · error
PUT failed: HTTP ` : ''}
Error message
PUT ${url} failed: HTTP ${response.status}${body ? ` — ${body.slice(0, 300)}` : ''}${response.status === 403 ? ' (need sntryu_ user token with project:write)' : ''} What it means
putErrorMessageFilters PUTs the merged filters:error_messages value to Sentry project options and throws on any non-ok response, including the status, response body excerpt, and a 403 hint that a sntryu_ user token with project:write is required. It fails fast rather than proceeding to read-back verification after a failed write.
Solutions
- Regenerate the sntryu_ user token ensuring both project:read and project:write scopes and update SENTRY_API_TOKEN.
- Confirm SENTRY_ORG/SENTRY_PROJECT point at the intended project.
- Inspect the body excerpt in the message for a 400 validation problem with the filter text.
- Run with --dry-run first to confirm the merged payload before PUT.
Example fix
// before SENTRY_API_TOKEN=sntryu_readonly_token // after // create user token with scopes: project:read, project:write SENTRY_API_TOKEN=sntryu_rw_token
Defensive patterns
Strategy: try-catch
Validate before calling
const scopes = decodeJwtScopes(token); // or document token scopes at creation
if (!scopes.includes('project:write')) throw new Error('Token missing project:write'); Try / catch
try { await putErrorMessageFilters(...); } catch (err) {
if (err.message.includes('HTTP 403')) throw new Error('Recreate token with project:read+project:write');
throw err;
} Prevention
- Create the user token with both project:read and project:write scopes up front.
- Run with --dry-run before any PUT.
- Keep write credentials out of read-only CI jobs.
When it happens
Trigger: Non-2xx from the PUT of project options: 403 because the token lacks project:write or is not a user token, 401 expired token, 404 wrong org/project, 400 payload rejected by Sentry validation.
Common situations: Read-only tokens used for a write operation, SENTRY_API_TOKEN created without the project:write scope, project slug typo, or Sentry rejecting the filter payload.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- GET failed: HTTP ` : ''}
- Read-back after PUT did not confirm expected filters.\n
- Sentry issues request failed: HTTP
- Convex embed key validation unavailable: http
- Expected an array of Sentry issues. A Sentry error body is…
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22).
Data as JSON: /api/errors/db9f3c4516bd8b6e.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/sync-sentry-convex-probe-filters.mjs:314
* @returns {Promise<Record<string, unknown>>}
*/
async function putErrorMessageFilters(token, org, project, filtersText) {
const url = `${SENTRY_HOST}/api/0/projects/${org}/${project}/`;
const response = await fetch(url, {
method: 'PUT',
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
options: { 'filters:error_messages': filtersText },
}),
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
});
if (!response.ok) {
const body = await response.text();
throw new Error(
`PUT ${url} failed: HTTP ${response.status}`
+ (body ? ` — ${body.slice(0, 300)}` : '')
+ (response.status === 403
? ' (need sntryu_ user token with project:write)'
: ''),
);
}
return /** @type {Record<string, unknown>} */ (await response.json());
}
/**
* Regenerate the committed internal probe filter list from source.
*
* @param {string[]} patterns
*/
function runWrite(patterns) {
mkdirSync(dirname(COMMITTED_LIST_PATH), { recursive: true });
writeFileSync(COMMITTED_LIST_PATH, formatCommittedFilterList(patterns), 'utf8');View on GitHub (pinned to e586b8b4b8)