iflytek/astron-agent · error · BusinessException
UNAUTHORIZED
UNAUTHORIZED
Error message
BusinessException(ResponseEnum.UNAUTHORIZED)
What it means
SkillSandboxConfigService.getRuntimeCredential validates the internal service token before issuing sandbox runtime credentials (provider, API key, timeout, internet flag). If no runtimeCredentialTokenProvider is configured or the presented serviceToken does not match, it throws UNAUTHORIZED. This is the service-to-service authentication gate for the sandbox runtime.
Solutions
- Ensure the sandbox runtime sends the exact configured service token header.
- Confirm the same token value is configured in both the toolkit service and the sandbox runtime (env var / config).
- Restart both services after rotating the token so in-memory providers pick up the new value.
- If the feature was just added, verify the runtimeCredentialTokenProvider bean is actually created and injected.
Example fix
// before
http.post("/skill-sandbox/credential", body); // no token header
// after
http.post("/skill-sandbox/credential", body,
Map.of("X-Service-Token", configuredServiceToken)); Defensive patterns
Strategy: validation
Validate before calling
if (serviceToken == null || !serviceToken.equals(expectedToken)) {
throw new IllegalArgumentException("service token missing or mismatched");
} Try / catch
try {
cred = runtimeClient.getCredential(token, flowId, uid, spaceId);
} catch (BusinessException e) {
if ("UNAUTHORIZED".equals(e.getCode())) rotateAndRetryWithFreshToken();
} Prevention
- Inject the service token via env var identically on both sides of the call.
- Rotate tokens with a coordinated redeploy, never one service at a time.
- Send the token header on every internal request (use a shared HTTP client interceptor).
- Assert at startup that the token is configured and non-empty.
When it happens
Trigger: Calling getRuntimeCredential (or its REST endpoint) with a null/missing/incorrect serviceToken, or with the runtime credential feature disabled so runtimeCredentialTokenProvider is null.
Common situations: Sandbox runtime calling with a stale or rotated token after redeployment; token env var not set on one side (provider never configured); caller omitting the Authorization/service-token header entirely.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- UNAUTHORIZED
- Skill resource URL is not allowed
- exceeds size limit
- UNAUTHORIZED
- INSUFFICIENT_PERMISSIONS
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/ef9b1a66f0a97bf2.
Report an issue: GitHub.
Appendix: source
Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/skill/SkillSandboxConfigService.java:133
SkillSandboxConfig config = getActiveConfig(uid, spaceId);
SkillSandboxRuntimeRefDto dto = new SkillSandboxRuntimeRefDto();
dto.setProvider(PROVIDER_E2B);
dto.setEnabled(config != null);
dto.setUid(uid);
dto.setSpaceId(spaceId);
return dto;
}
/**
* Resolve the E2B credential only for the authenticated private broker. A workflow reference
* derives scope from the database; standalone agent calls must provide a currently authorized
* uid/space pair.
*/
public SkillSandboxRuntimeCredentialDto getRuntimeCredential(
String serviceToken, String flowId, String uid, Long spaceId) {
if (runtimeCredentialTokenProvider == null
|| !runtimeCredentialTokenProvider.matches(serviceToken)) {
throw new BusinessException(ResponseEnum.UNAUTHORIZED);
}
assertExplicitScope(uid, spaceId);
SkillSandboxConfig config;
if (StringUtils.isNotBlank(flowId)) {
List<Workflow> workflows = workflowMapper.selectList(
Wrappers.lambdaQuery(Workflow.class)
.eq(Workflow::getFlowId, StringUtils.trim(flowId))
.eq(Workflow::getDeleted, Boolean.FALSE)
.last("limit 2"));
if (workflows == null || workflows.size() != 1) {
throw new BusinessException(ResponseEnum.WORKFLOW_NOT_EXIST);
}
Workflow workflow = workflows.getFirst();
assertWorkflowExecutionScope(workflow, uid, spaceId);
config = getActiveConfigForTrustedScope(workflow.getUid(), workflow.getSpaceId());
} else {
config = getActiveConfigForTrustedScope(uid, spaceId);
}View on GitHub (pinned to 5e758547a8)