paperclipai/paperclip · error

Challenge secret is required. Pass --token or --token-env.

Error message

Challenge secret is required. Pass --token or --token-env.

What it means

HTTP 404 with body {"error":"User secret definition not found"} from DELETE /api/companies/:companyId/user-secret-definitions/:definitionId (secrets.ts:691). svc.removeUserSecretDefinition(companyId, definitionId, ...) returned null: no definition matched the company+ID pair at removal time - already deleted, wrong company, or bad ID. Requires secret-definition admin rights.

Solutions

  1. Treat 404 on DELETE as 'already gone' when removal is the goal (idempotent semantics).
  2. Derive deletion targets from a fresh list call scoped to :companyId, not from stored IDs.
  3. Confirm the actor holds secret-definition admin in that company before scripting bulk deletes.
  4. Verify :companyId and definitionId pair correctness when copying between environments.

Example fix

// before
const res = await api.delete(`/api/companies/${companyId}/user-secret-definitions/${defId}`);
if (!res.ok) throw new Error('delete failed');

// after
const res = await api.delete(`/api/companies/${companyId}/user-secret-definitions/${defId}`);
if (res.status === 404) {
  logger.info(`definition ${defId} already absent in ${companyId}; done`);
} else if (!res.ok) {
  throw new Error(`delete failed: ${res.status}`);
}
Defensive patterns

Strategy: fallback

Validate before calling

async function deleteDefinitionSafe(api: ApiClient, companyId: string, definitionId: string) {
  const res = await api.fetch(`/api/companies/${companyId}/user-secret-definitions/${definitionId}`, {
    method: 'DELETE',
  });
  if (res.status === 404) return { deleted: true, alreadyGone: true };
  if (!res.ok) throw new Error(`delete failed: ${res.status}`);
  return { deleted: true, alreadyGone: false };
}

Type guard

function isApiErrorBody(body: unknown): body is { error: string } {
  return typeof body === 'object' && body !== null &&
    typeof (body as Record<string, unknown>).error === 'string';
}
const isDefinitionNotFound = (b: unknown): boolean =>
  isApiErrorBody(b) && b.error === 'User secret definition not found';

Try / catch

try {
  await api.delete(`/api/companies/${companyId}/user-secret-definitions/${defId}`);
} catch (err) {
  if (err instanceof ApiError && err.status === 404 && isDefinitionNotFound(err.body)) {
    return; // already removed - idempotent success
  }
  throw err;
}

Prevention

When it happens

Trigger: Double-delete of the same definition; deleting a definition whose ID was copied from another company; cleanup jobs re-running after a partial failure where the first pass already removed it.

Common situations: Idempotency-unaware deprovisioning scripts; multi-company admins pasting a definitionId under the wrong :companyId; UI retry of a delete that actually succeeded.

Related errors


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/c677f163b6b96e91. Report an issue: GitHub.

Appendix: source

Thrown at cli/src/commands/client/auth.ts:202

        }),
    );
  }
}

function parseJson(value: string): unknown {
  return JSON.parse(value) as unknown;
}

function resolveChallengeToken(opts: AuthChallengeOptions): string {
  const token = opts.token?.trim();
  if (token) return token;
  const envName = opts.tokenEnv?.trim();
  if (envName) {
    const envValue = process.env[envName]?.trim();
    if (envValue) return envValue;
    throw new Error(`Environment variable ${envName} is empty or not set.`);
  }
  throw new Error("Challenge secret is required. Pass --token or --token-env.");
}

View on GitHub (pinned to 120ae5428f)