ruvnet/ruflo · error
policy administration requires an authenticated user context
Error message
policy administration requires an authenticated user context
What it means
All mutating policy admin tools (policy_rule_upsert, policy_budget_set, policy_approve, policy_revoke) call requireAuthenticatedAdministrator(context), which asserts the tool-call context has principalType === 'user' AND a string principalId. Anything else — an agent principal, a service identity, or a missing context — throws this error before any policy state is loaded or written. It is an intentional authorization gate: agents may evaluate policy (policy_evaluate) but only authenticated humans may change it.
Solutions
- Route policy administration through a call path that supplies context = { principalType: 'user', principalId: '<user id>' }.
- Use policy_evaluate and policy_status for non-human callers — they have no admin requirement.
- If you are the MCP host, propagate the authenticated user's identity into the tool-call context before dispatching admin tools.
- Do not work around this by faking a user principal from agent code — the gate exists so agents cannot self-authorize (ADR-324).
Example fix
// before
await mcp.call('policy_rule_upsert', { rule: {...} }); // context undefined -> throws
// after
await mcp.callWithUser('policy_rule_upsert', { rule: {...} }, userPrincipal); // context = { principalType: 'user', principalId: user.id } Defensive patterns
Strategy: type-guard
Validate before calling
function hasUserPrincipal(context: unknown): boolean {
return typeof context === 'object' && context !== null &&
(context as any).principalType === 'user' &&
typeof (context as any).principalId === 'string';
}
if (!hasUserPrincipal(toolContext)) {
return { error: 'policy administration is reserved for authenticated humans; use policy_evaluate/policy_status instead' };
}
await callPolicyAdminTool(args, toolContext); Type guard
interface UserContext { principalId: string; principalType: 'user' }
function isUserContext(c: unknown): c is UserContext {
return typeof c === 'object' && c !== null &&
(c as Record<string, unknown>).principalType === 'user' &&
typeof (c as Record<string, unknown>).principalId === 'string' &&
(c as Record<string, unknown>).principalId.length > 0;
} Try / catch
try {
await callTool('policy_rule_upsert', args, context);
} catch (e) {
if (e instanceof Error && e.message === 'policy administration requires an authenticated user context') {
return { error: 'Sign in as a user principal to administer policy; agents may only evaluate it.' };
}
throw e;
} Prevention
- Gate admin policy tools in your UI/automation behind an explicit user-auth check before dispatch.
- Give agent/service callers only policy_evaluate and policy_status in their tool allowlist.
- Never synthesize a { principalType: 'user' } context from non-human code — it defeats the ADR-324 authorization boundary.
When it happens
Trigger: Invoking any of policy_rule_upsert / policy_budget_set / policy_approve / policy_revoke with context = undefined, context.principalType = 'agent', or principalId missing/non-string. This happens when the MCP client does not forward user identity in the tool-call context, or when an agent-driven automation tries to self-approve a policy change.
Common situations: MCP server wiring that never populates the principal context; scripts/CI calling admin policy tools with a service account; an agent loop attempting to escalate its own permissions via policy_approve (correctly blocked); upgrading from a version where these tools were unauthenticated.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- policy- : ; receipt=
- approval issuance requires an authenticated human identity…
- mcp-caller-auth-enabled-but-no-token
- mcp-caller-auth-verification-failed
- self-approval-forbidden
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/5cacf88d649b7b13.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/policy-tools.ts:24
PolicyState,
} from '@claude-flow/security';
import type { MCPTool } from './types.js';
import {
evaluatePolicyRequest,
issuePolicyApproval,
loadPolicyState,
revokePolicyApproval,
setPolicyBudget,
setPolicyMode,
upsertPolicyRule,
verifyPolicyLedger,
} from '../services/policy-runtime.js';
function requireAuthenticatedAdministrator(
context: Record<string, unknown> | undefined,
): asserts context is Record<string, unknown> & { principalId: string; principalType: 'user' } {
if (context?.principalType !== 'user' || typeof context.principalId !== 'string') {
throw new Error('policy administration requires an authenticated user context');
}
}
export const policyTools: MCPTool[] = [
{
name: 'policy_evaluate',
description: 'Evaluate an agent action against ADR-324 policy and persist a tamper-evident decision receipt. Use when a consequential tool, deployment, network, spend, or promotion action needs authorization.',
category: 'security',
inputSchema: {
type: 'object',
properties: {
request: { type: 'object', description: 'Policy request containing identity, action, and optional evidence/envelope context' },
},
required: ['request'],
},
handler: async (input, context) => evaluatePolicyRequest(
input.request as PolicyRequest,
typeof context?.projectRoot === 'string' ? context.projectRoot : process.cwd(),View on GitHub (pinned to fa13ee4ad6)