SonarSource/sonarqube · warning

Skipping project binding

Error message

Skipping project binding '{}' from search results: failed to build it: {} ({})

What it means

This warning is logged by ProjectBindingsServiceServerImpl.toProjectBindingOrSkip when building a single ProjectBinding from a ProjectAlmSettingDto throws any exception while processing a project-bindings search. Instead of failing the whole search, the binding's UUID is logged (with a sanitized message and exception class name only, never the raw throwable) and the binding is skipped from the results.

Solutions

  1. Identify the skipped binding UUID from the log and inspect its projectAlmSetting row via api/alm_settings/list or api/project_bindings/search with a filter
  2. Re-bind the affected project with api/alm_settings/set_binding using a valid ALM setting and repository identifiers
  3. Check that the referenced ALM setting still exists and its URL/credentials are valid
  4. If many bindings are skipped, check ALM server reachability from the SonarQube host

Example fix

// before: dangling binding
DELETE the orphan binding or re-bind
// after
curl -X POST -u admin:token 'https://sonar.example.com/api/alm_settings/set_binding' -d 'project=proj_key' -d 'almSettingKey=github1' -d 'almRepo=org/repo'
Defensive patterns

Strategy: validation

Validate before calling

// Before searching, confirm the binding's ALM setting still exists and repo fields are populated
AlmSettingDto setting = dbClient.almSettingDao().selectByKey(dbSession, almSettingKey);
boolean ok = setting != null && setting.getUrl() != null && projectAlmSetting.getAlmRepo() != null;

Try / catch

// Consumers of project_bindings/search: tolerate missing entries and re-check skipped UUIDs from logs
List<ProjectBinding> bindings = service.bindings(query);
if (bindings.size() < expected) { log.warn("Some bindings skipped; check server warnings for UUIDs"); }

Prevention

When it happens

Trigger: GET api/project_bindings/search (or equivalent internal search) where one project's binding references an ALM setting that is broken: deleted ALM setting row, unreachable ALM server, invalid stored url/repoId, or an ALM REST client throwing during live resolution.

Common situations: An ALM setting was deleted while projects were still bound to it; an ALM instance went down mid-search; legacy rows with null almRepo/almSlug; post-migration data inconsistencies.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/e6240745a5fdad0f. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ProjectBindingsServiceServerImpl.java:139

        .filter(Objects::nonNull)
        .toList();
      return new ProjectBindings(bindings, new PageRestResponse(1, bindings.size(), bindings.size()));
    }
  }

  /**
   * Same as {@link #toProjectBinding}, but never lets one binding's failure (e.g. a concurrently deleted ALM
   * setting) abort the whole search — unlike {@link #getProjectBinding}, which is about exactly one binding and
   * can afford to let such an inconsistency surface as an error.
   */
  @Nullable
  private ProjectBinding toProjectBindingOrSkip(DbSession dbSession, ProjectAlmSettingDto projectAlmSetting, Map<String, String> bitbucketCloudTokenCache) {
    try {
      return toProjectBinding(dbSession, projectAlmSetting, bitbucketCloudTokenCache);
    } catch (Exception e) {
      // Same rationale as resolveLive/persist: never log "e" directly, since an exception reaching this catch
      // could in principle carry ALM-influenced content from a lower layer.
      LOG.warn("Skipping project binding '{}' from search results: failed to build it: {} ({})", projectAlmSetting.getUuid(),
        sanitizeForLog(String.valueOf(e.getMessage())), e.getClass().getSimpleName());
      return null;
    }
  }

  private static void validateQuery(ProjectBindingsQuery query) {
    boolean hasProjectId = isNotBlank(query.projectId());
    boolean hasUrl = isNotBlank(query.url());
    boolean hasOrganizationId = isNotBlank(query.organizationId());
    boolean hasDevOpsPlatform = isNotBlank(query.devOpsPlatform());
    boolean hasRepositoryId = isNotBlank(query.repositoryId());

    if (hasDevOpsPlatform != hasRepositoryId) {
      throw BadRequestException.create("devOpsPlatform and repositoryId must be provided together");
    }
    int paramCount = (hasProjectId ? 1 : 0) + (hasUrl ? 1 : 0) + (hasOrganizationId ? 1 : 0) + (hasDevOpsPlatform ? 1 : 0);
    if (paramCount > 1) {
      throw BadRequestException.create("Only one of projectId, url, organizationId, or devOpsPlatform+repositoryId can be provided");

View on GitHub (pinned to 184c821202)