iflytek/astron-agent · warning · NumberFormatException

non-positive skill id

Error message

non-positive skill id

What it means

SkillEnrichmentService.enrichSkillEntries parses each skill entry's identifier as a positive long. If the id is missing, non-numeric, or <= 0, a NumberFormatException("non-positive skill id") is thrown internally and immediately caught: the offending entry is logged as a warning and removed from the array. The exception never escapes the method; it is a control-flow signal used to sanitize the skill list against attacker-controlled URLs.

Solutions

  1. Ensure every skill entry passed to enrichSkillEntries carries a positive numeric id.
  2. Check the log line 'Ignore invalid skill id' to identify which entry was dropped and why.
  3. If entries legitimately lack ids, fetch/assign server-derived ids before enrichment.
  4. If dropping is unexpected, validate the upstream payload shape before calling the service.

Example fix

// before
entries.add(Map.of("id", "temp-1", "url", "https://example.com"));
skillEnrichmentService.enrichSkillEntries(entries);

// after
entries.add(Map.of("id", 42, "url", "https://example.com"));
skillEnrichmentService.enrichSkillEntries(entries);
Defensive patterns

Strategy: validation

Validate before calling

const valid = entries.every(e => { const n = Number(e.id); return Number.isFinite(n) && n > 0; });
if (!valid) entries = entries.filter(e => Number(e.id) > 0);

Type guard

function hasPositiveNumericId(e) { const n = Number(e?.id); return Number.isFinite(n) && n > 0; }

Prevention

When it happens

Trigger: Calling enrichSkillEntries with a skill object whose `id` field is null, a non-numeric string, or a number <= 0; the entry is silently dropped from the returned list.

Common situations: Upstream API returns skill entries with string ids like "new" or "-1"; manually crafted responses from an untrusted skill source; schema drift where the id field was renamed or became an object.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/5269c10bd415104c. Report an issue: GitHub.

Appendix: source

Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/skill/SkillEnrichmentService.java:70

    public void enrichSkillEntries(JSONArray skillArray, String uid, Long spaceId) {
        if (skillArray == null || skillArray.isEmpty()) {
            return;
        }
        Set<Long> skillIds = new LinkedHashSet<>();
        for (int i = skillArray.size() - 1; i >= 0; i--) {
            Object obj = skillArray.get(i);
            if (!(obj instanceof Map skillObj)) {
                skillArray.remove(i);
                continue;
            }
            // Runtime metadata is server-derived. Remove every historical/client value before
            // looking at the id so no invalid or unauthorized entry can retain an attacker URL.
            SERVER_DERIVED_FIELDS.forEach(skillObj::remove);
            Object skillIdObj = skillIdentifier(skillObj);
            try {
                long skillId = Long.parseLong(String.valueOf(skillIdObj));
                if (skillId <= 0) {
                    throw new NumberFormatException("non-positive skill id");
                }
                skillIds.add(skillId);
            } catch (NumberFormatException ex) {
                log.warn("Ignore invalid skill id: {}", skillIdObj);
                skillArray.remove(i);
            }
        }
        if (skillIds.isEmpty()) {
            return;
        }
        List<Long> requestedIds = skillIds.stream().sorted().toList();
        List<SkillImportDto> imports = uid == null
                ? skillFileService.getSkillImportsByIds(requestedIds)
                : skillFileService.getSkillImportsByIds(requestedIds, uid, spaceId);
        Map<Long, SkillImportDto> importMap = Objects.requireNonNullElse(imports, List.<SkillImportDto>of())
                .stream()
                .filter(Objects::nonNull)
                .filter(item -> item.getId() != null)

View on GitHub (pinned to 5e758547a8)