KeygraphHQ/shannon · error · PentestError
Login instructions template not found
Error message
Login instructions template not found
What it means
Thrown by buildLoginInstructions when the shared login template file at <promptsBaseDir>/shared/login-instructions.txt does not exist (fs.pathExists returns false). The template is required whenever config.authentication is set, because interpolateVariables calls buildLoginInstructions to fill the {{LOGIN_INSTRUCTIONS}} placeholder. Category 'filesystem', non-retryable.
Source
Thrown at apps/worker/src/services/prompt-manager.ts:163
}
interface IncludeReplacement {
placeholder: string;
content: string;
}
// Pure function: Build complete login instructions from config
async function buildLoginInstructions(
authentication: Authentication,
logger: ActivityLogger,
promptsBaseDir: string = PROMPTS_DIR,
): Promise<string> {
try {
// 1. Load the login instructions template
const loginInstructionsPath = path.join(promptsBaseDir, 'shared', 'login-instructions.txt');
if (!(await fs.pathExists(loginInstructionsPath))) {
throw new PentestError('Login instructions template not found', 'filesystem', false, { loginInstructionsPath });
}
const fullTemplate = await fs.readFile(loginInstructionsPath, 'utf8');
const getSection = (content: string, sectionName: string): string => {
const regex = new RegExp(`<!-- BEGIN:${sectionName} -->([\\s\\S]*?)<!-- END:${sectionName} -->`, 'g');
const match = regex.exec(content);
return match?.[1]?.trim() ?? '';
};
// 2. Extract sections based on login type
const loginType = authentication.login_type?.toUpperCase();
let loginInstructions = '';
const commonSection = getSection(fullTemplate, 'COMMON');
const authSection = loginType ? getSection(fullTemplate, loginType) : ''; // FORM or SSO
const verificationSection = getSection(fullTemplate, 'VERIFICATION');
View on GitHub (pinned to 1ae0a142f8)
Solutions
- Verify the file exists in the running container: docker exec <worker> ls /app/apps/worker/prompts/shared/login-instructions.txt (npx) or check ./apps/worker/prompts/shared/login-instructions.txt (local).
- Restore or rebuild the prompts tree from the repo, then re-run.
- If using a promptDir override, confirm it resolves to a directory that still contains shared/login-instructions.txt.
- In local mode, ensure SHANNON_WORKER_ROOT is unset or correctly points at apps/worker so PROMPTS_DIR resolves to the full prompt set.
Example fix
// before: promptDir override omits shared/
// loadPrompt('vuln-injection', vars, config, false, logger, './my-prompts')
// -> buildLoginInstructions looks for ./my-prompts/shared/login-instructions.txt (missing)
// after: keep the standard prompts dir, or symlink the shared tree
// ln -s ./apps/worker/prompts/shared ./my-prompts/shared Defensive patterns
Strategy: validation
Validate before calling
// Before starting an authenticated scan, confirm the template is present and readable
import { pathExists } from 'fs-extra';
const templatePath = path.join(PROMPTS_DIR, 'shared', 'login-instructions.txt');
if (config.authentication && !(await pathExists(templatePath))) {
throw new Error(`Missing required login template at ${templatePath}`);
} Type guard
function hasAuthentication(auth: unknown): auth is Authentication {
return typeof auth === 'object' && auth !== null &&
('login_type' in auth || 'login_flow' in auth || 'credentials' in auth);
} Try / catch
try {
await loadPrompt('vuln-auth', vars, config, false, logger);
} catch (e) {
if (e instanceof PentestError && e.type === 'filesystem' && /Login instructions template not found/.test(e.message)) {
// restore the prompts tree in the image, then retry; otherwise disable auth and re-run unauthenticated
}
throw e;
} Prevention
- Build the worker image from a clean checkout so apps/worker/prompts/shared/ is always included.
- When overriding promptDir, point it at the full prompts tree, not a subset missing shared/.
- In local mode, leave SHANNON_WORKER_ROOT unset unless you know the prompts path resolution.
- Add a preflight check that asserts shared/login-instructions.txt exists when config.authentication is set.
When it happens
Trigger: A scan config declares an authentication block (config.authentication.login_flow or login_type) so interpolateVariables invokes buildLoginInstructions, but the prompts directory mounted into the worker container is missing shared/login-instructions.txt. Common in local mode when the prompts dir override (SHANNON_WORKER_ROOT or promptDir) points at a partial tree, or in a custom Docker image that did not copy apps/worker/prompts/.
Common situations: Running local mode with a promptDir override that omits the shared/ subfolder. A custom worker image that pruned prompt files. An npx image older than the code expecting a newer template. Mis-set SHANNON_WORKER_ROOT pointing at the repo root instead of the worker package.
Related errors
- Failed to build login instructions: ${errMsg}
- Prompt file not found: ${promptPath}
- CONFIG_NOT_FOUND
- CONFIG_VALIDATION_FAILED
- Variable interpolation failed: ${errMsg}
AI-assisted analysis of KeygraphHQ/shannon@1ae0a142f8 (2026-08-12).
Data as JSON: /api/errors/7ff32edfcd0f0d60.
Report an issue: GitHub.