paperclipai/paperclip · error · Error
/ / : required field needs placeholder
Error message
${app.slug}/${connectionMethod.key}/${connectionField.key}: required field needs placeholder What it means
validateApp() requires every required connection field (across tenantFields, extensionFields, credentialFields) that is not a checkbox to carry a placeholder — the example text shown in the connection form. Required non-checkbox fields without placeholder text would render an empty hint in the UI, so the definition is rejected.
Solutions
- Add a placeholder string to the required field describing the expected input format (e.g. placeholder: "sk-live-...").
- When using the field() helper, pass the placeholder as the third argument instead of leaving it undefined.
- If no example text makes sense (e.g. a toggle), change the field type to "checkbox" or make it optional.
Example fix
// before
{ key: "accountSid", label: "Account SID", required: true, type: "text" }
// after
{ key: "accountSid", label: "Account SID", required: true, type: "text", placeholder: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } Defensive patterns
Strategy: validation
Validate before calling
for (const m of app.methods) {
for (const f of [...(m.tenantFields ?? []), ...(m.extensionFields ?? []), ...(m.credentialFields ?? [])]) {
if (f.required && f.type !== 'checkbox' && !f.placeholder) throw new Error(`${m.key}/${f.key} needs a placeholder`);
}
} Type guard
const fieldOk = (f) => !f.required || f.type === 'checkbox' || (typeof f.placeholder === 'string' && f.placeholder.length > 0);
Prevention
- Always pass the placeholder argument to the field() helper; treat it as required in code review.
- Include a realistic format example in each placeholder (e.g. key prefixes, ID shapes).
- Run the ingest script before committing new fields — it catches this immediately.
When it happens
Trigger: A field object in tenantFields/extensionFields/credentialFields has required: true, type !== "checkbox", and placeholder missing/empty. Common with fields built inline as { key, label, required: true, type } without a placeholder property.
Common situations: Adding a new required tenant/credential field and forgetting the placeholder argument; the field() helper signature is (key, label, placeholder) so omitting the third argument yields undefined; a YAML/JSON-authored definition where placeholder was left blank for a required select/text field.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- : capture has no states
- / : oauth requires ownershipModes
- / : tool api_key requires keyPlacement
- : invalid AppDefinition
- A full lowercase source SHA is required.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/c8423c950ba9e764.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/ingest-app-definitions.mjs:1606
);
if (
connectionMethod.auth === "oauth" &&
connectionMethod.ownershipModes.length === 0
)
throw new Error(
`${app.slug}/${connectionMethod.key}: oauth requires ownershipModes`,
);
for (const connectionField of [
...(connectionMethod.tenantFields ?? []),
...(connectionMethod.extensionFields ?? []),
...(connectionMethod.credentialFields ?? []),
])
if (
connectionField.required &&
connectionField.type !== "checkbox" &&
!connectionField.placeholder
)
throw new Error(
`${app.slug}/${connectionMethod.key}/${connectionField.key}: required field needs placeholder`,
);
}
};
const captureFiles = definitionsOnly ? [] : fs
.readdirSync(corpus)
.filter((fileName) => fileName.endsWith(".md") && fileName !== "INDEX.md")
.sort();
if (!definitionsOnly && captureFiles.length !== 99)
throw new Error(`Expected 99 captures, found ${captureFiles.length}`);
const parsedCaptures = Object.fromEntries(
captureFiles.map((fileName) => [
path.basename(fileName, ".md"),
parseCapture(fileName),
]),
);
const reviewReport = {
schemaVersion: 1,View on GitHub (pinned to 3f1d897a7c)