paperclipai/paperclip · error · Error
Unsafe ${label}: ${result}
Error message
Unsafe ${label}: ${result} What it means
safeId validates that a string value (IDs, filenames, labels coming from config or JSON) matches the SAFE_ID pattern before it is used. Values containing path separators, spaces, or other unsafe characters are rejected with the label naming what field failed. It prevents untrusted roster/campaign content from injecting unsafe identifiers or paths.
Source
Thrown at packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs:45
"case.json",
"config.json",
]);
function json(value) {
return `${JSON.stringify(value, null, 2)}\n`;
}
async function loadObject(path) {
const value = JSON.parse(await readFile(path, "utf8"));
if (value === null || Array.isArray(value) || typeof value !== "object") {
throw new Error(`Expected a JSON object: ${path}`);
}
return value;
}
function safeId(value, label = "identifier") {
const result = String(value ?? "");
if (!SAFE_ID.test(result)) throw new Error(`Unsafe ${label}: ${result}`);
return result;
}
function inside(root, candidate, label) {
const rel = relative(resolve(root), resolve(candidate));
if (!rel || rel === ".." || rel.startsWith(`..${sep}`)) {
throw new Error(`${label} escapes its declared root`);
}
return resolve(candidate);
}
export function credentialForConfig(config) {
if (config.provider === "opencode") return "OPENROUTER_API_KEY";
if (config.provider === "claude_managed") return "ANTHROPIC_API_KEY";
if (config.provider === "aws_agentcore") return "AWS_AGENTCORE_OIDC";
if (config.provider === "codex" || config.provider === undefined) {
return "OPENAI_API_KEY";
}View on GitHub (pinned to 01ad858492)
Solutions
- Rename the offending identifier to a slug matching SAFE_ID (typically letters, digits, hyphens/underscores) in the campaign or roster JSON.
- Trim whitespace from the ID in the source file.
- Check the SAFE_ID regex in the script and conform the value to it exactly.
Example fix
// before "id": "Team Roster/v2" // after "id": "team-roster-v2"
Defensive patterns
Strategy: validation
Validate before calling
const SAFE_ID = /^[a-z0-9][a-z0-9-_]*$/i;
if (!SAFE_ID.test(String(id ?? ""))) throw new Error(`ID must match ${SAFE_ID}: ${id}`); Type guard
const isSafeId = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(v);
Try / catch
try {
id = safeId(rawId, "roster ID");
} catch (err) {
if (err.message.startsWith("Unsafe")) console.error(`Rename identifier to slug form: ${err.message}`);
throw err;
} Prevention
- Adopt slug-only naming (letters, digits, hyphens) for all IDs and roster filenames.
- Lint campaign/roster JSON IDs against SAFE_ID in CI.
- Avoid generating IDs from user text without slugification.
When it happens
Trigger: Calling safeId(value, label) where String(value ?? '') does not match SAFE_ID — e.g. roster.id = 'my roster/v2', a campaign ID with spaces or dots, or an empty string.
Common situations: Hand-edited campaign/roster JSON with IDs containing slashes, spaces, or uppercase-unfriendly characters; a filename with '..' passed as a roster reference; copy-pasted IDs with trailing whitespace or special characters.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Invalid normalized OpenCode session id
- Invalid Discord form token
- sandbox runtime asset key is not a simple path segment: ${ke
- Invalid GitHub launcher run ID
- device-login promotion: the account identifier cannot form a
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/0963d1c38ec78e08.
Report an issue: GitHub.