theonedev/onedev · error · BadRequestException

Invalid http method for manifest pull: ${method}

Error message

Invalid http method for manifest pull: ${method}

What it means

The manifest endpoint supports GET/HEAD (pull), PUT (push), and DELETE (remove by tag); any other method falls through to this BadRequestException. The message is phrased for the pull path but applies to any unsupported method on /v2/<name>/manifests/<reference>.

Source

Thrown at server-plugin/server-plugin-pack-container/src/main/java/io/onedev/server/plugin/pack/container/ContainerServlet.java:430

							packBlobService.downloadBlob(manifestInfo.getLeft(), manifestInfo.getRight(), baos);
							bytes = baos.toByteArray();
							response.setContentType(new ContainerManifest(bytes).getMediaType());
							if (method.equals("GET")) 
								response.getOutputStream().write(bytes);
						} else {
							throw new NotFoundException(ErrorCode.MANIFEST_UNKNOWN);
						}
						break;
					case "DELETE":
						sessionService.run(() -> {
							var project = checkProject(projectPath, true);
							if (isTag(reference))
								packService.deleteByNameAndVersion(project, TYPE, repository, reference);
							response.setStatus(SC_ACCEPTED);
						});
						break;
					default:
						throw new BadRequestException("Invalid http method for manifest pull: " + method);
				}
			} else if ((matcher = compile("(.+)/([^/]+)/tags/list").matcher(pathInfo)).matches()) {
				var projectPath = matcher.group(1);
				var repository = matcher.group(2);
				sessionService.run(() -> {
					var project = checkProject(projectPath, false);

					var result = new HashMap<String, Object>();
					result.put("name", projectPath + "/" + repository);
					var tags = new ArrayList<String>();
					result.put("tags", tags);

					int count = Integer.MAX_VALUE;
					var countParam = request.getParameter("n");
					if (countParam != null)
						count = Integer.parseInt(countParam);
					if (count != 0) {
						var lastTag = request.getParameter("last");

View on GitHub (pinned to d44925c47c)

Solutions

  1. Pull with GET (or HEAD), push with PUT, delete with DELETE on /v2/<name>/manifests/<reference>.
  2. Replace POST/PATCH usage with a full PUT of the complete manifest.
  3. Note DELETE only works by tag; deleting by digest is not supported here — delete via tag or prune server-side.
  4. Fix client URL/method mapping for the OCI distribution API.

Example fix

// before
POST /v2/app/manifests/1.0  body=manifest.json
// after
PUT /v2/app/manifests/1.0  body=manifest.json
Defensive patterns

Strategy: validation

Validate before calling

const MANIFEST_METHODS = ['GET','HEAD','PUT','DELETE'];
if (!MANIFEST_METHODS.includes(method)) {
  throw new Error(`/manifests/ accepts GET/HEAD/PUT/DELETE only, got ${method}`);
}

Prevention

When it happens

Trigger: Methods other than GET/HEAD/PUT/DELETE sent to /v2/<name>/manifests/<reference> — e.g. POST to the manifests URL, PATCH attempting partial manifest updates, or OPTIONS from preflight scanners.

Common situations: Clients using POST-based creation (some generic REST wrappers); tooling attempting PATCH-style manifest editing, which OCI does not allow; security probes hitting the manifests route.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/7d989f34dcd5a1f0. Report an issue: GitHub.