paperclipai/paperclip · error

Company ID is required. Pass --company-id, set…

Error message

Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set context profile companyId via `paperclipai context set`.

What it means

HTTP 404 with body {"error":"User secret value not found"} from PATCH /api/companies/:companyId/me/user-secrets/:secretId (secrets.ts:786). svc.updateCurrentUserSecretValue(companyId, ownerUserId, secretId, ...) returned null: no secret value row matches the triple (company, current board user, secretId). The lookup is owner-scoped - a secretId belonging to another user 404s even within the same company - and it also 404s if the value was deleted or never created for this user.

Solutions

  1. Confirm the current user actually has a value: list this user's secrets for the company and check secretId is present.
  2. If absent, create the value first (the create/upsert path), then patch it.
  3. Never assume a shared secretId works across users - values are per-owner.
  4. After any user secret deletion, refresh the local list before further edits.

Example fix

// before
await api.patch(`/api/companies/${companyId}/me/user-secrets/${secretId}`, { value: newValue });

// after
const mine = await api.listMyUserSecrets(companyId);
if (!mine.some((s) => s.id === secretId)) {
  await api.createUserSecretValue(companyId, secretId, newValue); // first setValue path
} else {
  await api.patch(`/api/companies/${companyId}/me/user-secrets/${secretId}`, { value: newValue });
}
Defensive patterns

Strategy: validation

Validate before calling

async function upsertMySecretValue(api: ApiClient, companyId: string, secretId: string, value: unknown) {
  const mine = await api.fetch(`/api/companies/${companyId}/me/user-secrets`);
  const list = await mine.json();
  const has = (Array.isArray(list) ? list : list.items ?? []).some(
    (s: { id: string }) => s.id === secretId,
  );
  if (!has) {
    // no value row for this user yet: create first, then patch later
    return api.createUserSecretValue(companyId, secretId, value);
  }
  return api.fetch(`/api/companies/${companyId}/me/user-secrets/${secretId}`, {
    method: 'PATCH',
    body: JSON.stringify({ value }),
  });
}

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 isValueNotFound = (b: unknown): boolean =>
  isApiErrorBody(b) && b.error === 'User secret value not found';

Try / catch

try {
  await api.patch(`/api/companies/${companyId}/me/user-secrets/${secretId}`, { value });
} catch (err) {
  if (err instanceof ApiError && err.status === 404 && isValueNotFound(err.body)) {
    // this user has no value for that id (or it was just deleted): create it
    await api.createUserSecretValue(companyId, secretId, value);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Patching a secret value the current user never set (definition exists, but this user has no value row); patching after the user removed their value; using someone else's secretId gleaned from logs; :companyId mismatch with where the value was stored.

Common situations: Profile/settings UIs preloading a value that another session just cleared; onboarding flows that PATCH before the initial PUT/POST of the secret value; scripts replaying captured secret IDs across user accounts.

Related errors


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

Appendix: source

Thrown at cli/src/commands/client/common.ts:68

  opts?: { requireCompany?: boolean },
): ResolvedClientContext {
  const context = readContext(options.context);
  const { name: profileName, profile } = resolveProfile(context, options.profile);

  const apiBase = resolveApiBase(options, profile);

  const resolvedApiKey = resolveApiKey(options, profile);
  const explicitApiKey = resolvedApiKey.value;
  const storedBoardCredential = explicitApiKey ? null : getStoredBoardCredential(apiBase);
  const apiKey = explicitApiKey || storedBoardCredential?.token;

  const companyId =
    options.companyId?.trim() ||
    process.env.PAPERCLIP_COMPANY_ID?.trim() ||
    profile.companyId;

  if (opts?.requireCompany && !companyId) {
    throw new Error(
      "Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set context profile companyId via `paperclipai context set`.",
    );
  }

  // Agent-authenticated mutations (checkout, release, interactions, PATCH of an
  // in-progress issue) require the X-Paperclip-Run-Id header (the server returns
  // "401 Agent run id required" without it). Source it from --run-id, else the
  // PAPERCLIP_RUN_ID env the adapter/embodiment context already exports.
  const runId = options.runId?.trim() || process.env.PAPERCLIP_RUN_ID?.trim() || undefined;

  const api = new PaperclipApiClient({
    apiBase,
    apiKey,
    runId,
    recoverAuth: explicitApiKey || !canAttemptInteractiveBoardAuth()
      ? undefined
      : async ({ error }) => {
          const requestedAccess = error.message.includes("Instance admin required")

View on GitHub (pinned to 120ae5428f)