ruvnet/ruflo · error · TypeError
Unsupported federation authorization mode: ${String(mode)}
Error message
Unsupported federation authorization mode: ${String(mode)} What it means
createFederationClaimChecker() accepts exactly three authorization modes: 'legacy' (the default when mode is omitted), 'observe', and 'enforce'. Any other value throws a TypeError at construction time, before any claim is evaluated. This is a programmer/config error, not a runtime data condition.
Source
Thrown at v3/@claude-flow/plugin-agent-federation/src/application/claim-checker.ts:45
/**
* Compatibility bridge for the legacy federation policy engine.
*
* This removes the anonymous `() => true` production stub and makes
* compatibility behavior explicit:
* - legacy: preserve pre-ADR-325 behavior;
* - observe: calculate and report missing grants without blocking;
* - enforce: default deny unless the exact claim is configured.
*
* ADR-324 policy adapters can supply the `grantedClaims` set after evaluating
* the request; ownership-changing federation messages remain disabled in the
* default message policy until the full ingress PEP is composed.
*/
export function createFederationClaimChecker(
config: FederationClaimCheckerConfig = {},
): FederationClaimChecker {
const mode = config.mode ?? 'legacy';
if (mode !== 'legacy' && mode !== 'observe' && mode !== 'enforce') {
throw new TypeError(`Unsupported federation authorization mode: ${String(mode)}`);
}
const grantedClaims = new Set<FederationClaimType>();
for (const claim of config.grantedClaims ?? []) {
if (!FEDERATION_CLAIMS.has(claim as FederationClaimType)) {
throw new TypeError(`Unknown federation claim: ${claim}`);
}
grantedClaims.add(claim as FederationClaimType);
}
return {
mode,
grantedClaims,
checkClaim: (claim) => {
const granted = grantedClaims.has(claim);
if (mode === 'observe') config.onObservation?.(claim, granted);
return mode !== 'enforce' || granted;
},View on GitHub (pinned to fa13ee4ad6)
Solutions
- Use exactly one of the literals 'legacy', 'observe', or 'enforce'
- If mode comes from env/config, trim and lowercase it, then validate against the allowed set before constructing
- Type the config field as the union 'legacy' | 'observe' | 'enforce' so TypeScript rejects bad literals at compile time
- Align package versions if the mode name genuinely exists in a newer release
Example fix
// before
createFederationClaimChecker({ mode: 'Enforce' as any });
// after
const mode = 'enforce' as const;
createFederationClaimChecker({ mode }); Defensive patterns
Strategy: type-guard
Validate before calling
const MODES = ['legacy', 'observe', 'enforce'] as const;
const raw = (config.mode ?? 'legacy').toString().trim().toLowerCase();
const mode = (MODES as readonly string[]).includes(raw)
? (raw as (typeof MODES)[number])
: 'legacy';
createFederationClaimChecker({ ...config, mode }); Type guard
type FederationAuthMode = 'legacy' | 'observe' | 'enforce';
function isFederationAuthMode(m: unknown): m is FederationAuthMode {
return m === 'legacy' || m === 'observe' || m === 'enforce';
} Prevention
- Type config fields as the literal union so bad values fail at compile time
- Normalize env-sourced modes: trim + lowercase before use
- Fail fast in your config loader with a list of valid modes
- Keep package versions aligned so mode vocabularies match
When it happens
Trigger: Typos such as 'enforced' or 'stric'; wrong casing like 'Enforce'; mode strings sourced from env vars or config files with trailing whitespace; mode names from a newer package version passed to an older one.
Common situations: Env-var-driven policy configuration without normalization; configs copied from documentation of a different release; ADR-324 adapters emitting their own mode vocabulary.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Unknown federation claim: ${claim}
- unknown game "${key}". Known: ${Object.keys(GAMES).join(', '
- unknown strategy "${name}". Available: ${roster.map((r) => r
- localCompute: no adapter for graphId=${input.graphId}
- signBacktestArtifact: privateKey must be 32 bytes (got ${pri
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/acee9085d66c2e00.
Report an issue: GitHub.