KeygraphHQ/shannon · error · PentestError
Variables must include webUrl and repoPath
Error message
Variables must include webUrl and repoPath
What it means
Second validation guard in interpolateVariables: the variables object must be truthy and contain both webUrl and repoPath (the two placeholders {{WEB_URL}} and {{REPO_PATH}} are always substituted). Missing either throws with category 'validation', non-retryable, recording which keys were present in context.variables. This is an internal-contract check on the caller (loadPrompt builds these from CLI/pipeline input).
Source
Thrown at apps/worker/src/services/prompt-manager.ts:325
// Pure function: Variable interpolation
async function interpolateVariables(
template: string,
variables: PromptVariables,
config: DistributedConfig | null = null,
logger: ActivityLogger,
promptsBaseDir: string = PROMPTS_DIR,
): Promise<string> {
try {
if (!template || typeof template !== 'string') {
throw new PentestError('Template must be a non-empty string', 'validation', false, {
templateType: typeof template,
templateLength: template?.length,
});
}
if (!variables || !variables.webUrl || !variables.repoPath) {
throw new PentestError('Variables must include webUrl and repoPath', 'validation', false, {
variables: Object.keys(variables || {}),
});
}
// replaceLiteral is used for all value insertions so config values that
// contain `$&`/`$$`/`$1`/etc. aren't mangled as replacement patterns.
let result = template;
result = replaceLiteral(result, /{{WEB_URL}}/g, variables.webUrl);
result = replaceLiteral(result, /{{REPO_PATH}}/g, variables.repoPath);
result = replaceLiteral(result, /{{PLAYWRIGHT_SESSION}}/g, variables.PLAYWRIGHT_SESSION || 'agent1');
result = replaceLiteral(result, /{{AUTH_CONTEXT}}/g, buildAuthContext(config));
result = replaceLiteral(
result,
/{{DESCRIPTION}}/g,
config?.description ? `Description: ${config.description}` : '',
);
const avoidUrlRules = config?.avoid?.filter((r) => r.type !== 'code_path') ?? [];View on GitHub (pinned to 1ae0a142f8)
Solutions
- Inspect context.variables on the thrown PentestError to see which keys were supplied.
- Ensure the scan start command passes both -u <url> and -r <repo> so the pipeline receives webUrl and repoPath.
- If resuming, verify session.json contains session.webUrl and that the repo mount is present before the workflow restarts.
- Patch the caller to default/validate these fields before calling loadPrompt.
Example fix
// before: variables missing repoPath
// await loadPrompt('recon', { webUrl: 'https://t' }, config, false, logger);
// after: supply both required fields
// await loadPrompt('recon', { webUrl: 'https://t', repoPath: '/workspace/repo' }, config, false, logger); Defensive patterns
Strategy: validation
Validate before calling
// Ensure both required variables are present before loading a prompt
function hasRequiredVars(v: Partial<PromptVariables>): v is PromptVariables {
return typeof v.webUrl === 'string' && v.webUrl.length > 0 &&
typeof v.repoPath === 'string' && v.repoPath.length > 0;
}
if (!hasRequiredVars(variables)) {
throw new Error(`Missing required prompt variables: webUrl/repoPath`);
} Type guard
function hasWebUrlAndRepoPath(v: unknown): v is { webUrl: string; repoPath: string } {
return typeof v === 'object' && v !== null &&
typeof (v as any).webUrl === 'string' && (v as any).webUrl.length > 0 &&
typeof (v as any).repoPath === 'string' && (v as any).repoPath.length > 0;
} Try / catch
try {
await loadPrompt(name, variables, config, false, logger);
} catch (e) {
if (e instanceof PentestError && /Variables must include webUrl and repoPath/.test(e.message)) {
// pipeline-input reconstruction is incomplete — rebuild vars from session.json
variables = { webUrl: session.session.webUrl, repoPath: expectedRepoPath, ...variables };
}
throw e;
} Prevention
- Always start scans with both -u <url> and -r <repo>.
- When reconstructing PromptVariables during resume, persist and reload webUrl/repoPath from session.json.
- Type-narrow variables at the boundary with hasWebUrlAndRepoPath before passing to loadPrompt.
- Unit-test prompt loading with a fully populated variables object.
When it happens
Trigger: loadPrompt (or a direct interpolateVariables caller) is invoked with a PromptVariables object where webUrl or repoPath is undefined/empty — e.g. the pipeline started without a target URL, the repo path was not mounted, or a resume path reconstructed variables incompletely.
Common situations: A pipeline-input bug omits repoPath when running a phase that does not need the repo but still interpolates prompts. Resume logic rebuilds variables from session.json but a field was never persisted. A test harness calls loadPrompt with a partial variables object.
Related errors
- Template must be a non-empty string
- Failed to build login instructions: ${errMsg}
- Path traversal detected in @include(): ${rawPath}
- Variable interpolation failed: ${errMsg}
- CONFIG_VALIDATION_FAILED
AI-assisted analysis of KeygraphHQ/shannon@1ae0a142f8 (2026-08-12).
Data as JSON: /api/errors/76dac083420a0bff.
Report an issue: GitHub.