pbakaus/impeccable · error
surface brief requires a concrete project-relative primary t
Error message
surface brief requires a concrete project-relative primary target or URL
What it means
Thrown by writeSurfaceBrief() when normalizeSurfaceTarget(primaryTarget) returns null. normalizeSurfaceTarget rejects empty/non-string values, malformed URLs (catch falls to null), route: strings that don't start with '/' or contain '..', and filesystem paths whose path.relative() to projectRoot escapes the project (starts with '..'). The brief writer needs one concrete, resolvable primary target so it can slug the file and emit frontmatter.
Source
Thrown at plugin/skills/impeccable/scripts/lib/surface-briefs.mjs:134
const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot });
const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized)));
if (exact) return { brief: exact, candidates: briefs, reason: 'slug' };
const mapped = briefs.filter((brief) => brief.targets.includes(normalized));
return {
brief: mapped.length === 1 ? mapped[0] : null,
candidates: mapped.length > 1 ? mapped : briefs,
reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found',
};
}
export function writeSurfaceBrief({
projectRoot = process.cwd(),
primaryTarget,
relatedTargets = [],
body,
}) {
const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot });
if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL');
const normalizedRelated = [...new Set(relatedTargets
.map((target) => normalizeSurfaceTarget(target, { projectRoot }))
.filter((target) => target && target !== normalizedPrimary))];
const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot });
const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot });
fs.mkdirSync(path.dirname(filePath), { recursive: true });
const frontmatter = [
'---',
`version: ${SURFACE_BRIEF_VERSION}`,
`slug: ${JSON.stringify(slug)}`,
`primary_target: ${JSON.stringify(normalizedPrimary)}`,
`related_targets: ${JSON.stringify(normalizedRelated)}`,
'---',
].join('\n');
fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8');
return filePath;
}
View on GitHub (pinned to d14711ae3d)
Solutions
- Pass a non-empty string that is either an http(s) URL, a route: string starting with '/', an absolute path inside the project, or a relative path that stays within projectRoot.
- Pre-validate with the exported normalizeSurfaceTarget() before calling writeSurfaceBrief; if it returns null, surface a user-facing error instead of letting the throw propagate.
- For URL inputs, new URL() it yourself first to catch malformed URLs early; trim whitespace and drop fragments/search before passing.
- Confirm projectRoot matches the directory the target path is relative to (the default is process.cwd()).
Example fix
// before
writeSurfaceBrief({ primaryTarget: userHref, body });
// after
import { normalizeSurfaceTarget, writeSurfaceBrief } from './lib/surface-briefs.mjs';
const normalized = normalizeSurfaceTarget(userHref, { projectRoot });
if (!normalized) throw new Error(`Invalid primary target: ${JSON.stringify(userHref)}`);
writeSurfaceBrief({ projectRoot, primaryTarget: normalized, body }); Defensive patterns
Strategy: validation
Validate before calling
import { normalizeSurfaceTarget } from './lib/surface-briefs.mjs';
function isValidSurfaceTarget(target, projectRoot) {
return typeof target === 'string'
&& target.trim().length > 0
&& normalizeSurfaceTarget(target, { projectRoot }) !== null;
}
// call before writeSurfaceBrief
if (!isValidSurfaceTarget(primaryTarget, projectRoot)) {
throw new Error(`Refusing to write brief: invalid primary target ${JSON.stringify(primaryTarget)}`);
} Type guard
/** Narrowing guard for surface brief targets. */
function isSurfaceTarget(value, projectRoot) {
if (typeof value !== 'string' || !value.trim()) return false;
// Reuse the library's own normalizer as the source of truth.
try { return normalizeSurfaceTarget(value, { projectRoot }) !== null; }
catch { return false; }
} Try / catch
try {
writeSurfaceBrief({ projectRoot, primaryTarget, relatedTargets, body });
} catch (err) {
if (/concrete project-relative primary target/.test(err.message)) {
// prompt the user/agent for a valid target instead of crashing
return { ok: false, error: 'invalid_primary_target', hint: 'Pass a project-relative path, route:/x, or http(s) URL.' };
}
throw err;
} Prevention
- Always normalise through the exported normalizeSurfaceTarget() — it encodes every rejection rule (URL parse, route '..', path escape).
- When forwarding user/agent hrefs, trim and new URL() them first so malformed URLs surface as your own error.
- Keep projectRoot explicit rather than relying on process.cwd() so path-relative checks match your intent.
When it happens
Trigger: Calling writeSurfaceBrief({ primaryTarget: '' }), with undefined, with 'route:../x', with 'https:// bad url' (URL constructor throws -> null), or with an absolute path like '/etc/passwd' that resolves outside projectRoot. Also when a relative path equals '.' or resolves to the projectRoot itself (rel === '.' -> null).
Common situations: Programmatically building a brief from user/agent input without pre-validating; passing a DOM href that is a bare fragment ('#foo') or protocol-relative ('//host'); wrong cwd/projectRoot so a legitimately in-project file computes as outside; passing an empty string after trimming. URL inputs with stray whitespace or invalid characters hit the URL constructor catch.
Related errors
- invalid catalog: ${validation.errors.join('; ')}
- concept-seed: --scope must be direction or surface
- concept-seed: --reroll must be a non-negative integer
- concept-seed: --register must be safer or bolder
- concept-seed: --register steers a re-roll round; pass --rero
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/98343324e3d70b9e.
Report an issue: GitHub.