can1357/oh-my-pi · error · OmpTypeError

alias "${name}" is declared as both public and private

Error message

alias "${name}" is declared as both public and private

What it means

In a scope, every alias key maps to a canonical visible name (a leading `#` marks the alias private and is stripped). Two source keys that reduce to the same visible name — e.g. `"foo"` and `"#foo"` — collide, and omptype throws when building the scope's alias table.

Source

Thrown at packages/omptype/src/type.ts:3791

	materialized: boolean;
}

function isRuntimeModule(value: unknown): value is RuntimeModule {
	return typeof value === "object" && value !== null && MODULE_SCOPE in value;
}

function buildScope(aliases: Record<string, unknown>, options?: ScopeOptions): TypeScope {
	const scopeMeta: TypeMeta = options?.clone === undefined ? EMPTY_META : { clone: options.clone };
	const withScopeConfig = (ir: IR): IR =>
		options?.divisor === undefined ? ir : configureSelected(ir, options.divisor, { kind: "divisor" });
	const entries = new Map<string, ScopeAlias>();
	for (const sourceName in aliases) {
		const isPrivate = sourceName.startsWith("#");
		const visibleName = isPrivate ? sourceName.slice(1) : sourceName;
		const declaration = parseGenericDeclaration(visibleName);
		const external = isRuntimeGeneric(aliases[sourceName]) ? aliases[sourceName] : undefined;
		const name = declaration?.name ?? visibleName;
		if (entries.has(name)) throw new OmpTypeError(`alias "${name}" is declared as both public and private`);
		entries.set(name, {
			name,
			sourceName,
			private: isPrivate,
			genericParameters: declaration?.parameters ?? external?.[GENERIC_META].parameters,
			definition: aliases[sourceName],
			generic: external,
			materialized: false,
		});
	}

	const references = new Map<string, IR>();
	const targets = new Map<string, IR>();
	let scopeValue: TypeScope;

	const materialize = (entry: ScopeAlias): unknown => {
		if (entry.materialized) return entry.definition;
		entry.materialized = true;

View on GitHub (pinned to 9690622007)

Solutions

  1. Delete the duplicate key from the aliases object passed to `type.scope`.
  2. Rename one of the colliding aliases.
  3. Normalize keys (strip/standardize `#` prefixes) before building the scope object when keys are dynamic.

Example fix

// before
type.scope({ "foo": "string", "#foo": "number" })
// after
type.scope({ "foo": "string", "#bar": "number" })
Defensive patterns

Strategy: validation

Validate before calling

function hasAliasCollision(aliases: Record<string, unknown>) { const seen = new Set(); for (const k of Object.keys(aliases)) { const n = k.startsWith('#') ? k.slice(1) : k; if (seen.has(n)) return true; seen.add(n); } return false; }

Try / catch

try { const $ = type.scope(aliases); } catch (e) { if (e instanceof OmpTypeError && e.message.includes('both public and private')) dedupeAliases(aliases); else throw e; }

Prevention

When it happens

Trigger: Defining a scope with both `{ "foo": ..., "#foo": ... }`, or constructing scope keys dynamically so one ends up with a `#` prefix duplicating an existing public alias.

Common situations: Merging alias maps from multiple modules where one exports a private variant and another a public one with the same name; template-driven scope generation that conditionally prefixes `#`.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/24a43f49d9be572f. Report an issue: GitHub.