clockworklabs/SpacetimeDB · error · Exception
InvalidTableVisibility
InvalidTableVisibility
Error message
Table row type visibility must be public or internal, including containing types.
What it means
Compile-time error from the SpacetimeDB C# module source generator. When scanning a [Table] row type, Module.cs walks the type and all its containing types and only accepts Accessibility.Public, Internal, ProtectedAndInternal or NotApplicable; any private/protected/protected-or-internal nesting level makes the generator report InvalidTableVisibility and throw, aborting code generation. The row type must be at least as visible as every containing type.
Source
Thrown at crates/bindings-csharp/Codegen/Module.cs:536
var container = context.TargetSymbol;
Visibility = container.DeclaredAccessibility;
while (container != null)
{
switch (container.DeclaredAccessibility)
{
case Accessibility.ProtectedAndInternal:
case Accessibility.NotApplicable:
case Accessibility.Internal:
case Accessibility.Public:
if (Visibility < container.DeclaredAccessibility)
{
Visibility = container.DeclaredAccessibility;
}
break;
default:
diag.Report(ErrorDescriptor.InvalidTableVisibility, typeSyntax);
throw new Exception(
"Table row type visibility must be public or internal, including containing types."
);
}
container = container.ContainingType;
}
TableAccessors = new(
context.Attributes.Select(a => new TableAccessor(this, a, diag)).ToImmutableArray()
);
Indexes = new(
context
.TargetSymbol.GetAttributes()
.Where(TableIndex.CanParse)
.Select(a => new TableIndex(this, a, diag))
.ToImmutableArray()
);
}View on GitHub (pinned to 3653d2ed49)
Solutions
- Make the [Table] row type `public` (simplest and always accepted)
- If it is nested, make every containing class `public` or `internal` as well
- Rebuild after the change so the source generator re-runs and emits the table boilerplate
Example fix
// before
public static class Module {
[Table]
private class Player { public uint Id; } // InvalidTableVisibility
}
// after
public static class Module {
[Table]
public class Player { public uint Id; }
} Defensive patterns
Strategy: validation
Validate before calling
// Check before adding [Table]: row type and all containers must be public/internal
static bool IsTableVisible(Type t)
{
for (var c = t; c is not null; c = c.DeclaringType)
if (c.IsNestedPrivate || c.IsNestedFamily || c.IsNestedFamORAssem)
return false; // private / protected / protected-or-internal are rejected
return true;
} Prevention
- Default to `public` for all [Table] row types and their containing classes
- Keep STDB generator diagnostics visible in the build (don't suppress STDB analyzers)
- Watch the IDE error list, not just compiler output — the generator reports before it throws
When it happens
Trigger: Putting [Table]/[Table(name, public=true)] on a private nested class, a protected nested class, or a public class nested inside a private class; declaring the row type with `internal` inside a `private` outer type also fails because Visibility must be raised to the container's accessibility.
Common situations: New module projects keeping tables nested inside a Program/Module helper class marked private; refactoring that moves tables into internal helper classes; copying sample code where the enclosing class lost its public modifier; Unity-style file-scoped nesting habits applied to SpacetimeDB modules.
Related errors
- Invalid procedure signature.
- Invalid HTTP handler signature.
- Expected an interpolated string as a lambda body but got {in
- Could not resolve type {type}
- Unique index point scan returned >1 rows
AI-assisted analysis of clockworklabs/SpacetimeDB@3653d2ed49 (2026-08-20).
Data as JSON: /api/errors/4be800cdbc10c8da.
Report an issue: GitHub.