{"record":{"id":"053cb7298c86e26a","repo":"koala73/worldmonitor","slug":"initramp-ramp-already-configured-active-exist","errorCode":null,"errorMessage":"[initRamp] ramp already configured (active=${existing.active}, tier=${existing.currentTier}). Run abortRamp first if reconfiguring.","messagePattern":"\\[initRamp\\] ramp already configured \\(active=(.+?), tier=(.+?)\\)\\. Run abortRamp first if reconfiguring\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/rampRunner.ts","lineNumber":189,"sourceCode":"    const offset = args.waveLabelOffset ?? 0;\n    const hasSeedBroadcast = !!args.seedLastWaveBroadcastId;\n    const hasSeedSentAt = typeof args.seedLastWaveSentAt === \"number\";\n    if (hasSeedBroadcast !== hasSeedSentAt) {\n      throw new Error(\n        \"[initRamp] seedLastWaveBroadcastId and seedLastWaveSentAt must be provided together.\",\n      );\n    }\n    if (offset > 0 && !hasSeedBroadcast) {\n      throw new Error(\n        `[initRamp] waveLabelOffset=${offset} signals resumption after manual waves; seedLastWaveBroadcastId + seedLastWaveSentAt are required so the first cron tick can apply the kill-gate against the prior wave. Pass them, or set waveLabelOffset=0 to start a fresh ramp.`,\n      );\n    }\n    const existing = await ctx.db\n      .query(\"broadcastRampConfig\")\n      .withIndex(\"by_key\", (q) => q.eq(\"key\", RAMP_KEY))\n      .first();\n    if (existing) {\n      throw new Error(\n        `[initRamp] ramp already configured (active=${existing.active}, tier=${existing.currentTier}). Run abortRamp first if reconfiguring.`,\n      );\n    }\n    await ctx.db.insert(\"broadcastRampConfig\", {\n      key: RAMP_KEY,\n      active: true,\n      rampCurve: args.rampCurve,\n      currentTier: -1,\n      waveLabelPrefix: args.waveLabelPrefix,\n      waveLabelOffset: offset,\n      bounceKillThreshold:\n        args.bounceKillThreshold ?? DEFAULT_BOUNCE_KILL_THRESHOLD,\n      complaintKillThreshold:\n        args.complaintKillThreshold ?? DEFAULT_COMPLAINT_KILL_THRESHOLD,\n      killGateTripped: false,\n      lastWaveBroadcastId: args.seedLastWaveBroadcastId,\n      lastWaveSentAt: args.seedLastWaveSentAt,\n      lastWaveLabel: args.seedLastWaveLabel,","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/rampRunner.ts#L171-L207","documentation":"Thrown by initRamp when a broadcastRampConfig row already exists for RAMP_KEY. initRamp is a one-shot setup that refuses to overwrite an existing active or inactive config — the operator must run abortRamp first to clear the prior config before reconfiguring mid-launch. This prevents silently swapping a live ramp's curve/state.","triggerScenarios":"Calling initRamp when a config row for RAMP_KEY already exists (active or paused); reconfiguring a ramp without first aborting the existing one; a prior initRamp succeeded and a second call is attempted.","commonSituations":"Operator wants to change the ramp curve mid-launch; a previous ramp was paused (not aborted) and a new init is attempted; accidental double-invocation of initRamp.","solutions":["Run abortRamp first to clear the existing config, then call initRamp.","If the existing ramp is still desired, do not call initRamp — use the appropriate tuning mutation instead.","Confirm via the broadcastRampConfig row that abortRamp actually removed the config before retrying init."],"exampleFix":"// before\nawait initRamp(ctx, { rampCurve: [...], waveLabelPrefix: \"w\" }); // ramp already configured\n// after — abort first, then init\nawait abortRamp(ctx, {});\nawait initRamp(ctx, { rampCurve: [...], waveLabelPrefix: \"w\" });","handlingStrategy":"validation","validationCode":"const existing = await ctx.db.query(\"broadcastRampConfig\")\n  .withIndex(\"by_key\", q => q.eq(\"key\", RAMP_KEY)).first();\nif (existing) {\n  await abortRamp(ctx, {});\n}\nawait initRamp(ctx, args);","typeGuard":null,"tryCatchPattern":"try {\n  await initRamp(ctx, args);\n} catch (e) {\n  if (e.message.includes(\"ramp already configured\")) {\n    await abortRamp(ctx, {});\n    await initRamp(ctx, args);\n  } else throw e;\n}","preventionTips":["Always run abortRamp before initRamp when reconfiguring.","Check for an existing broadcastRampConfig row in a preflight.","Document the abort-then-init sequence in the operator runbook."],"tags":["convex","broadcast","ramp","idempotency","config","state-guard"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}