anomalyco/sst · error · VisibleError

Component name ${args.name} is not unique.

Error message

Component name ${args.name} is not unique.

What it means

SST links resources to functions by their component name, so every top-level linkable component must have a unique name (case-insensitive). `Link.reset()` tracks names in a Set during stack registration and throws this VisibleError when a second top-level SST or linkable-wrapped component reuses a name already seen.

Source

Thrown at platform/src/components/link.ts:69

    // Ensure component names are unique
    runtime.registerStackTransformation((args) => {
      const isLinkable =
        args.type.startsWith("sst:") ||
        Linkable.wrappedResources.has(args.type);
      if (isLinkable && !args.opts.parent) {
        const lcname = args.name.toLowerCase();

        // "App" is reserved and cannot be used as a component name.
        if (lcname === "app") {
          throw new VisibleError(
            `Component name "${args.name}" is reserved. Please choose a different name for your "${args.type}" component.`,
          );
        }

        // Ensure linkable resources have unique names. This includes all SST components
        // and non-SST components that are linkable.
        if (links.has(lcname)) {
          throw new VisibleError(`Component name ${args.name} is not unique.`);
        }
        links.add(lcname);
      }
      return {
        opts: args.opts,
        props: args.props,
      };
    });

    // Create link refs
    runtime.registerStackTransformation((args) => {
      const resource = args.resource;
      process.nextTick(() => {
        if (Link.isLinkable(resource) && !args.opts.parent) {
          try {
            const link = resource.getSSTLink();
            new Ref(args.name, args.type, link.properties, link.include);
          } catch (e) {}

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Rename one of the duplicate components to a unique name in sst.config.ts.
  2. If components are generated in a loop, interpolate a unique suffix: `new sst.aws.Function(`Fn${i}`, ...)`.
  3. Remember names are case-insensitive for this check ("api" vs "Api" still collide).
  4. If this appeared after a rename, note Pulumi treats a new name as a new resource; you may need to delete the old resource or use `pulumi state rename` to keep state clean.

Example fix

// before
const fn1 = new sst.aws.Function("Api", { handler: "src/a.handler" });
const bucket = new sst.aws.Bucket("api");
// after
const fn1 = new sst.aws.Function("Api", { handler: "src/a.handler" });
const bucket = new sst.aws.Bucket("Assets", {});
Defensive patterns

Strategy: validation

Validate before calling

const used = new Set<string>();
function uniqueName(name: string) {
  const key = name.toLowerCase();
  if (used.has(key)) throw new Error(`Component name "${name}" is not unique.`);
  used.add(key);
  return name;
}

Type guard

function isNameTaken(name: string, existing: Iterable<string>): boolean {
  return new Set([...existing].map((n) => n.toLowerCase())).has(name.toLowerCase());
}

Prevention

When it happens

Trigger: Declaring two top-level components with the same name in any casing, e.g. `new sst.aws.Function("Api", ...)` and `new sst.aws.Bucket("api", ...)` in sst.config.ts; only applies to types starting with `sst:` or wrapped linkable resources with no parent.

Common situations: Copy-pasting a component block and forgetting to rename it; loops that generate components with a fixed name literal instead of an index; merging two stacks that each had a component with the same name; renaming a variable but not the component name string (Pulumi names must also change to avoid state conflicts).

Related errors


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