gleam-lang/gleam · error

invalid constants can not be in an untyped ast

Error message

invalid constants can not be in an untyped ast

What it means

An internal invariant panic in Gleam's constant inference (`constant.rs:535`, `do_infer`). Constants carrying `Constant::Invalid` are sentinel nodes produced by the parser/error-recovery for syntactically invalid constants; they should be filtered out before type inference runs. Reaching inference with one means the untyped AST still contains an error placeholder, so inference cannot proceed and panics.

Solutions

  1. Fix the syntax error in the offending constant definition in your Gleam source — the compiler's error output should point near it.
  2. Rewrite the constant with valid literal/constructor syntax and recompile.
  3. If you believe the constant is valid, report a compiler bug with the source snippet — `Invalid` nodes should have produced a parse error or been filtered, not panicked.
  4. If developing the compiler, ensure `Constant::Invalid` nodes are filtered from the AST before inference, or make `do_infer` return a type error instead of panicking.

Example fix

// before (Gleam source with invalid constant)
const x: Int = not_a_valid_constant(]

// after
const x: Int = 42
Defensive patterns

Strategy: validation

Validate before calling

// Rust (compiler-side): filter invalid constants before inference
let constants: Vec<_> = constants
    .into_iter()
    .filter(|c| !matches!(c, Constant::Invalid { .. }))
    .collect();

Type guard

fn is_inferable(c: &TypedConstant) -> bool {
    !matches!(c, Constant::Invalid { .. })
}

Try / catch

// Panics are not recoverable; gate inference on the guard
if !is_inferable(constant) {
    return; // or emit a diagnostic instead of inferring
}

Prevention

When it happens

Trigger: Type inference (`infer`, `do_infer`, `infer_record`, `infer_tuple`, `infer_list`, `infer_string_concatenation`) encounters a `Constant::Invalid` node — e.g. a module constant or record/tuple/list element whose constant expression failed to parse but was not dropped before typing.

Common situations: Compiling a module with a malformed constant (bad literal, invalid record constructor syntax) where error recovery leaves an `Invalid` placeholder in a nested constant position; compiler development adding new constant forms; compiler bugs where `Invalid` nodes propagate into nested structures.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@49f8762da5 (2026-09-14). Data as JSON: /api/errors/3ac838b78d2b4b45. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/type_/constant.rs:553

                            .problems
                            .error(convert_unify_error(error, message.location()));
                    }
                    Box::new(message)
                });

                // Constant todos always result in a compile time error, this
                // way the developer has to remember to change them before
                // running their code!
                self.typer.problems.error(Error::TodoConstant { location });

                Constant::Todo {
                    location,
                    type_,
                    message,
                }
            }

            Constant::Invalid { .. } => panic!("invalid constants can not be in an untyped ast"),
        }
    }

    fn infer_string_concatenation(
        &mut self,
        location: SrcSpan,
        operator_start: u32,
        left: UntypedConstant,
        right: UntypedConstant,
        operator: BinOp,
    ) -> TypedConstant {
        self.typer
            .track_feature_usage(FeatureKind::ConstantStringConcatenation, location);
        let left = self.do_infer(left);

        if let Err(error) = unify(string(), left.type_()) {
            self.typer.problems.error(
                error

View on GitHub (pinned to 49f8762da5)