jsforce / jsforce/jsforce

tooling.destroy() with an array of ids always fails

Open
#1,815 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
1.5k
Forks
551
Avg merge
9h 2m
Merged PRs (30d)
3

Description

Description

conn.tooling.destroy(type, ids) fails with NOT_FOUND: The requested resource does not exist whenever ids is an array, even an array of one element. The same call with a single id string works fine.

Reproduction
const conn = new jsforce.Connection({ instanceUrl, accessToken });

// works: DELETE /services/data/v65.0/tooling/sobjects/Flow/301S800001CkHexIAF
await conn.tooling.destroy('Flow', '301S800001CkHexIAF');

// always fails with "The requested resource does not exist":
await conn.tooling.destroy('Flow', ['301S800001CkHexIAF'], { allOrNone: false });
Root cause

Connection.destroy() routes array input to _destroyMany(), which builds a sObject Collections URL:

// lib/connection.js
async _destroyMany(type, ids, options) {
  ...
  let url = [this._baseUrl(), 'composite', 'sobjects?ids='].join('/') + ids.join(',');

The Tooling class copies destroy / _destroyMany verbatim from Connection.prototype:

// lib/api/tooling.js
const { ..., destroy, _destroySingle, _destroyParallel, _destroyMany, ... } = Connection.prototype;

and its _baseUrl() appends /tooling, so the request becomes:

DELETE /services/data/v65.0/tooling/composite/sobjects?ids=...

The Tooling API has no sObject Collections resource (/composite/sobjects exists only in the regular REST API), so Salesforce returns 404 / The requested resource does not exist. The multi-record code paths for create() and update() are inherited the same way and should hit the same problem.

Expected behavior

Either:

  • Tooling overrides _destroyMany / _createMany / _updateMany to use _destroyParallel-style unitary calls, or batches through the Tooling composite endpoint (POST /services/data/vXX.X/tooling/composite, max 25 subrequests), or
  • the array form throws a clear "not supported by the Tooling API" error instead of a confusing 404.
Workaround

Call destroy per id, or build a Tooling composite request manually:

await conn.requestPost(`/services/data/v${conn.version}/tooling/composite`, {
  allOrNone: false,
  compositeRequest: ids.map((id, i) => ({
    method: 'DELETE',
    url: `/services/data/v${conn.version}/tooling/sobjects/Flow/${id}`,
    referenceId: `ref${i}`,
  })),
});

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lib/connection.js by tracing _destroyMany and the related multi-record create and update paths, then compare how lib/api/tooling.js inherits them and changes _baseUrl(). Determine a supported array behavior for the Tooling API, and verify that array operations no longer produce the invalid composite/sobjects 404 or instead return a clear unsupported error.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.