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
- Ensure every skill entry passed to enrichSkillEntries carries a positive numeric id.
- Check the log line 'Ignore invalid skill id' to identify which entry was dropped and why.
- If entries legitimately lack ids, fetch/assign server-derived ids before enrichment.
- 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
- Validate upstream skill payloads for a positive numeric id before enrichment.
- Monitor the 'Ignore invalid skill id' warnings to catch provider schema drift.
- Assign server-derived ids to local/unsaved skills before enrichment.
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
- model.encryptionFailed
- Encrypted data cannot be empty
- Skill resource is unavailable
- Remote resource URL is malformed
- Invalid JSON format for 'params
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)