anomalyco/sst · error · VisibleError

Cannot set both "logging.retention" and "logging.logGroup"

Error message

Cannot set both "logging.retention" and "logging.logGroup"

What it means

normalizeLogging() in the SST Function component validates that you don't configure the log group in two ways at once. `logging.retention` implicitly creates a log group for you, so it conflicts with an explicitly provided `logging.logGroup`.

Source

Thrown at platform/src/components/aws/function.ts:1943

          }
          if (streaming) {
            result.SST_FUNCTION_STREAMING = "true";
          }
        }
        return result;
      });
    }

    function normalizeStreaming() {
      return output(args.streaming).apply((streaming) => streaming ?? false);
    }

    function normalizeLogging() {
      return output(args.logging).apply((logging) => {
        if (logging === false) return undefined;

        if (logging?.retention && logging?.logGroup) {
          throw new VisibleError(
            `Cannot set both "logging.retention" and "logging.logGroup"`,
          );
        }

        if (args.durable && logging?.format && logging?.format != "json") {
          throw new VisibleError(
            `Durable functions require "logging.format" to be set to "json"`,
          );
        }

        const defaultFormat = args.durable ? "json" : "text";

        return {
          logGroup: logging?.logGroup,
          retention: logging?.retention ?? "1 month",
          format: logging?.format ?? defaultFormat,
        };
      });

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Remove `logging.retention` and keep `logging.logGroup` if you manage your own log group
  2. Or remove `logging.logGroup` and keep `retention` to let SST create the log group

Example fix

// before
logging: { retention: "1 week", logGroup: myGroup }
// after
logging: { logGroup: myGroup }
Defensive patterns

Strategy: validation

Validate before calling

if (args.logging && typeof args.logging === "object" && "retention" in args.logging && "logGroup" in args.logging)
  throw new Error("Set either logging.retention or logging.logGroup, not both");

Type guard

function hasLoggingConflict(logging: unknown): boolean {
  return !!logging && typeof logging === "object" &&
    "retention" in logging && "logGroup" in logging;
}

Try / catch

try {
  const fn = new sst.aws.Function("Fn", args);
} catch (e) {
  if ((e as Error).message.includes("logging.retention")) {
    console.error("Choose one: managed log group (retention) or existing logGroup");
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing both `logging: { retention: "1 week", logGroup: someLogGroup }` in Function args.

Common situations: Migrating from retention-based logging to a pre-provisioned log group (e.g. shared with CloudWatch alarms) but forgetting to delete the retention field; code-generated configs that set all logging keys unconditionally.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/a5efaaa54e2d1101. Report an issue: GitHub.