airbnb/epoxy · error · IllegalStateException

Nullability has not been set

Error message

Nullability has not been set

What it means

AttributeInfo tracks whether an Epoxy model attribute is nullable via a nullable Boolean. isNullable() is only valid after nullability has been explicitly resolved (hasSetNullability()); if processors or extensions query nullability before it was set, the annotation processor throws this IllegalStateException to prevent silently defaulting to non-null.

Solutions

  1. Call hasSetNullability() first and skip or defer isNullable() when false.
  2. Ensure the attribute builder sets nullability (setNullable or package-derived) before the attribute is used.
  3. If writing a processor extension, move the nullability query to a later processing step after EpoxyProcessor resolves it.
  4. Upgrade epoxy-processor — versions may resolve nullability earlier for your attribute kind.

Example fix

// before
val nullable = attributeInfo.isNullable() // throws if unset

// after
val nullable = if (attributeInfo.hasSetNullability()) attributeInfo.isNullable() else false
Defensive patterns

Strategy: type-guard

Type guard

fun AttributeInfo.nullabilityOrNull(): Boolean? =
  if (hasSetNullability()) isNullable() else null

Prevention

When it happens

Trigger: A processor extension or custom validator calls attributeInfo.isNullable() before setNullable/derivation ran — e.g. reading attributes in a custom step before EpoxyProcessor's nullability resolution stage.

Common situations: Custom Epoxy annotation-processor plugins hooking into the wrong processing round; forked/patched EpoxyProcessor code paths; new attribute types added to the processor without setting nullability in the builder.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13). Data as JSON: /api/errors/158327cd75d48720. Report an issue: GitHub.

Appendix: source

Thrown at epoxy-processor/src/main/java/com/airbnb/epoxy/processor/AttributeInfo.kt:177

        var implicit: CodeBlock? = null

        val isPresent: Boolean
            get() = explicit != null || implicit != null

        val isEmpty: Boolean
            get() = !isPresent

        fun value(): CodeBlock? = explicit ?: implicit
    }

    protected fun setJavaDocString(docComment: String?) {
        javaDoc = docComment?.trim()
            ?.let { if (it.isNotEmpty()) CodeBlock.of(it) else null }
    }

    fun isNullable(): Boolean {
        if (!hasSetNullability()) {
            throw IllegalStateException("Nullability has not been set")
        }

        return isNullable == true
    }

    fun hasSetNullability(): Boolean = isNullable != null

    fun getterCode(): String = if (isPrivate) getterMethodName!! + "()" else fieldName

    // Special case to avoid generating recursive getter if field and its getter names are the same
    fun superGetterCode(): String =
        if (isPrivate) String.format("super.%s()", getterMethodName) else fieldName

    fun setterCode(): String =
        (if (isGenerated) "this." else "super.") +
            if (isPrivate)
                setterMethodName!! + "(\$L)"
            else

View on GitHub (pinned to e45bd3a61f)