dagger/dagger · error · IntrospectionError

type ${JSON.stringify(typeDef)} has already been resolved, i

Error message

type ${JSON.stringify(typeDef)} has already been resolved, it should not be overwritten ; reference: ${JSON.stringify(reference)}

What it means

During TypeDef reference propagation, resolveTypeDef reached a TypeDef that already had its inner `typeDef` filled in, so resolving it again with the given reference would overwrite an already-resolved value. This is an internal invariant violation: it indicates a reference graph where the same TypeDef is being resolved twice or references were applied in the wrong order.

Source

Thrown at sdk/typescript/src/module/introspector/typescript_module/typedef_utils.ts:38

  return true
}

export function resolveTypeDef(
  typeDef: TypeDef<TypeDefKind> | undefined,
  reference: TypeDef<TypeDefKind>,
): TypeDef<TypeDefKind> {
  if (typeDef === undefined) {
    return reference
  }

  if (typeDef.kind === TypeDefKind.ListKind) {
    const listTypeDef = typeDef as TypeDef<TypeDefKind.ListKind>

    listTypeDef.typeDef = resolveTypeDef(listTypeDef.typeDef, reference)
    return listTypeDef
  }

  throw new IntrospectionError(
    `type ${JSON.stringify(typeDef)} has already been resolved, it should not be overwritten ; reference: ${JSON.stringify(reference)}`,
  )
}

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Remove self-referential/cyclic type definitions between your @object classes; Dagger does not support recursive types.
  2. Ensure each @object class field maps to a distinct TypeDef instance; avoid reusing one shared object across multiple fields.
  3. Reproduce with a minimal module and, if it looks like an SDK bug, report it to dagger with the module source.
  4. Upgrade the TypeScript SDK to the latest version, as this may be a fixed introspector regression.

Example fix

// before
class Node { child?: Node } // recursive

// after
class Child { name!: string }
class Node { child?: Child } // no self-reference
Defensive patterns

Strategy: validation

Validate before calling

// reject recursive object graphs before registering
function assertNoRecursion(root: Function, seen = new Set<Function>()): void {
  if (seen.has(root)) throw new Error(`recursive type: ${root.name}`)
  seen.add(root)
  // walk fields' types and recurse for @object classes
}

Try / catch

try {
  await dag.module().throwError()
} catch (e) {
  if (String(e).includes("has already been resolved")) {
    // indicates cyclic/self-referential type; refactor the object graph
  }
  throw e
}

Prevention

When it happens

Trigger: A TypeDef structure is passed through propagateReferences/resolveTypeDef twice (e.g. the same object instance appears in two fields, or a cyclic/self-referential object causes a second traversal), so the leaf TypeDef already has a resolved inner type when resolution recurses into it.

Common situations: Modules with recursive object types (an object referencing itself), duplicated registration of the same class under two fields, or SDK-internal bugs when mutating shared TypeDef instances.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/7fb484053d75042f. Report an issue: GitHub.